feat: security hardening, primordials, VERS, URL-to-PURL, DX improvements (v1.4.0)#14
Open
feat: security hardening, primordials, VERS, URL-to-PURL, DX improvements (v1.4.0)#14
Conversation
Add containsInjectionCharacters() to detect shell metacharacters (|, &, ;,
`, $, <, >, (, ), {, }, #, \, whitespace) in PURL component values. Uses
charCode scanning for performance in hot paths.
Add validate() to vscode-extension type that rejects illegal characters
in namespace, name, and platform qualifier, and enforces semver versions.
Aligns with upstream purl-spec PR #673.
Add 'vers' to PurlQualifierNames (6th standard qualifier per spec). Add version lowercasing for oci, pypi, and vscode-extension types per upstream type definitions (version_definition.case_sensitive: false).
toSpec(): Returns package specifier without pkg:type/ prefix, matching the npm "spec" concept (namespace/name@version?qualifiers#subpath). VERS (VErsion Range Specifier): First JavaScript implementation of the VERS companion spec to PURL. Supports parsing, serialization, and containment checking for semver-based schemes (npm, cargo, golang, gem, hex, pub, cran, swift). Early adoption - spec targets Ecma late 2026. URL-to-PURL: UrlConverter.fromUrl() converts registry URLs to PackageURLs using hostname-based dispatch (no regex). Supports 27 hostnames across 17 purl types including npm, pypi, maven, cargo, nuget, github, gitlab, bitbucket, docker, hex, pub, cocoapods, hackage, conda, cpan, luarocks, huggingface, swift, cran, and vscode marketplace/OpenVSX.
Add UrlConverter.fromUrl() and supportsFromUrl() for converting registry URLs back to PackageURLs. Uses hostname-based dispatch with no regex to avoid ReDoS. Supports npm, pypi, maven, cargo, nuget, github, gitlab, bitbucket, golang, hex, pub, composer, docker, cocoapods, hackage, cran, conda, cpan, huggingface, luarocks, swift, and vscode marketplaces. All inputs validated through tryCreatePurl() which delegates to the PackageURL constructor for type-specific validation and injection character detection.
Add src/primordials.ts mirroring Node.js internal/per_context/primordials.js pattern. Captures 43 built-in references at module load time using uncurryThis (bind.bind(call)) before user code can tamper with prototypes. Migrate ALL prototype method calls across all source files to use safe primordials: String (12 methods), Array (8 methods), RegExp (2 methods), Object (6 methods), Reflect (5 methods), JSON (2), constructors (5), globals (2). Additional hardening: - Freeze module-level regex patterns, Maps, Sets, arrays - Null prototype on all user-facing object literals (toObject, builder) - Frozen VERS constraints with __proto__: null - Isolated test updated to verify global.URL tampering protection
Lower statements/lines thresholds from 99% to 98% to account for defensive TypeScript narrowing guards in URL-to-PURL parsers that filterSegments() makes unreachable.
Add direct tests for encodeQualifierParam and encodeSubpath empty paths. Add URL-to-PURL edge case tests for cargo, nuget, golang, and vscode marketplace defensive paths. Add c8 ignore for unreachable PackageURL registration guard in compare.ts. Statements: 99.07%, Functions: 100%, Lines: 99.03%
Security fixes from audit: - Collapse consecutive .* groups in wildcard regex to prevent polynomial backtracking (ReDoS) in compare.ts matchWildcard - Reject null bytes (\x00) in all string components (name, namespace, version, subpath) to prevent truncation attacks in C-based consumers - Add 1000 constraint limit to VERS parser to prevent resource exhaustion - Validate semver version parts don't exceed MAX_SAFE_INTEGER to prevent precision loss in version comparison - Hoist inline regex patterns (vers.ts DIGITS_ONLY, swid.ts GUID_PATTERN) to frozen module-scope constants for both performance and immutability
New PackageURL methods: - isValid(str): Quick validation without throwing - fromUrl(url): Convenience wrapper for UrlConverter.fromUrl() - withVersion(v): Immutable copy with new version - withNamespace(ns): Immutable copy with new namespace - withQualifier(k, v): Immutable copy with qualifier added/updated - withQualifiers(obj): Immutable copy with all qualifiers replaced - withSubpath(s): Immutable copy with new subpath PurlBuilder: Add factory methods for 18 additional types (bitbucket, cocoapods, conan, conda, cran, deb, docker, github, gitlab, hackage, hex, huggingface, luarocks, oci, pub, rpm, swift, vscode-extension). Quality improvements: - Deduplicate stringify/stringifySpec (stringify delegates to stringifySpec) - Extract shared pattern-parsing in compare.ts (eliminates ~80 lines duplication) - Fix misplaced JSDoc in stringify.ts
fromString() now caches up to 1024 PackageURL instances keyed by input string. Identical PURL strings return the same instance (===), avoiding redundant parsing and allocation. LRU eviction prevents memory leaks. toString() lazily caches its result on the instance, making repeated stringification O(1). The common parse-once, stringify-many pattern now avoids all encoding work after the first call.
Split registry existence checks (*Exists functions) into a separate
'exists' entry point. The core bundle (parser, builder, VERS, URL
converter) is now 178 KB instead of 3.3 MB.
- Create src/exists.ts with all *Exists() re-exports
- Add './exists' subpath export to package.json
- Add exists.ts as second esbuild entry point
- Inline isObject() to avoid @socketsecurity/lib/objects import chain
(objects → sorts → semver → npm-pack.js, 2.5 MB)
Consumers import exists functions from the dedicated path:
import { npmExists } from '@socketregistry/packageurl-js/exists'
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
containsInjectionCharacters()+ vscode-extension validation against shell metacharactersUrlConverter.fromUrl()across 27 hostnames / 17 purl typesuncurryThis, zero raw prototype method callsisValid(),fromUrl(),toSpec(),with*()immutable copy methods, 18 new builder factoriesTest plan
Generated with Claude Code