Release v0.1.2 \u2014 dist-tag precision + native-binary script defaults#11
Merged
Conversation
…hanges
v0.1.0 / v0.1.1 wrote on-disk cache entries that under v0.1.1 produce
two classes of false positive that survive a 'brew upgrade':
* 'install-time lifecycle script `prepare` declared' for ~80
packages. v0.1.1 dropped 'prepare' from LIFECYCLE_SCRIPTS, but
cached LifecycleScripts signals still list it.
* 'signal provider `npm-registry` unavailable: decode: error
decoding response body' for the React 19 family. v0.1.1 added a
custom 'deserialize_deprecated' helper, but cached
Signal::Unavailable entries from the failed fetch are still
served from disk.
The cache already stamps a SCHEMA_VERSION on every entry and drops
entries whose schema doesn't match. Bumping the constant from 1 to 2
forces every entry written under the pre-fix binaries to be refetched
on first use under v0.1.2. No tree-name change is needed.
This is the cache-side complement to the v0.1.1 npm-registry fixes,
not a behaviour change in its own right.
The DistTagAnomaly signal fires whenever the npm registry's 'latest' dist-tag points to a version that is strictly older than the highest published non-prerelease version. That is structurally suspicious only for consumers who are *behind* what 'latest' advertises; consumers who pinned to the very version 'latest' points at are fully up to date with the publisher's stated current release and are not actually affected by the gap to the higher published line. Real-world examples in a 1276-package scan that this change silences: attr-accept@2.2.5 latest=2.2.5 highest=3.0.0 get-intrinsic@1.3.0 latest=1.3.0 highest=1.3.1 In both cases the publisher kept 'latest' on the older release line on purpose; the user followed it; nothing is wrong from the user's perspective. Without this guard InstallGuard reports a high-severity block that no remediation can resolve short of upgrading to a release line the maintainer has not blessed as default. The signal itself is still emitted (and still feeds the trust score / audit log) so an upcoming version-traversal heuristic can detect 'we're on latest *but* latest moved backwards from a previously-higher value' once we track packument history. This commit only changes the policy layer's Reason emission. New test 'dist_tag_anomaly_suppressed_when_resolved_equals_latest' locks in the precision fix; existing 'dist_tag_anomaly_blocks_by_default' continues to cover the genuine case (resolved 1.0.0, latest 1.1.0, highest 2.0.0).
…undary Patch- and minor-level drift inside one major version is overwhelmingly intentional LTS-line maintenance, not a publisher compromise. The single largest source of false-positive blocks in the latest 1276-package real-world scan was Storybook keeping 'latest=8.6.14' while '8.6.18' is published \u2014 the same release line, just an older patch held as the supported default while '9.x' rides 'next'. 13 of 21 remaining blocks were the same @storybook/* family. A genuine compromised-account 'rollback' attack ships a patch or minor *under* the existing major (e.g. publishing 1.4.5 after 'latest' was 1.5.0). That now-suppressed case is exactly what we no longer flag here \u2014 we accept the tradeoff because the signal as it stands cannot distinguish it from intentional LTS holds without packument history. Once we cache the prior 'latest' value we can re-introduce the same-major case as a separate, history-aware signal that fires only when 'latest' regressed. The cross-major case (latest on 1.x, 2.x is the highest published) remains the structural high-precision signal we surface today and is unchanged. Existing test 'dist_tag_anomaly_detects_latest_pointing_backwards' already exercises it (latest 1.1.0 vs highest 2.0.0); two new tests lock in the new narrowing: - dist_tag_anomaly_quiet_for_same_major_drift (8.6.x case) - dist_tag_anomaly_fires_across_major_boundary (regression guard)
…ages
Under the default DenyByDefault scripts policy, every CI run on
modern JS projects produced multiple high-severity blocks for
packages whose install/postinstall scripts are documented,
load-bearing, and well-known to the community \u2014 esbuild
(downloads native bundler), fsevents (macOS file-watcher native
binary), msw (copies the service worker into public/), cypress,
playwright, puppeteer, electron, sharp, bcrypt, node-gyp,
node-pre-gyp. Users had no remediation short of either
'allow-by-default' (negating the gate) or hand-curating
scripts.allow in every project's installguard.yaml.
Add a built-in DEFAULT_SCRIPT_ALLOWLIST consulted by
script_allowed() alongside the user-supplied scripts.allow. Same
shape as the typo ALLOWLIST shipped in v0.1.1: sorted slice,
binary_search lookup, sortedness enforced by a unit test.
Inclusion criteria for additions (documented in the const's
rustdoc):
* \u2265 1M weekly downloads on npm
* single, well-understood install purpose documented in the
package README
* no historical takeover advisory tied to the install script
* removing the script breaks the package (so users can't
realistically vendor a no-scripts fork)
Per-package, not per-(package, script): if a listed package adds
a *new* lifecycle script in a future version, VersionSurfaceChange
fires independently and surfaces the addition. The allowlist
concerns only 'this package legitimately uses install scripts',
not 'trust any future additions blindly'.
The existing 'scripts_deny_by_default' test now uses
my-private-tool as the user-allowed sample so it genuinely
exercises the user-supplied path; the previous esbuild example
overlapped with the new built-in default. New tests:
- default_script_allowlist_covers_native_binary_packages
- default_script_allowlist_still_blocks_arbitrary_packages
- default_script_allowlist_is_sorted_for_binary_search
Bumps workspace and all path-version pins to 0.1.2 and writes the
0.1.2 CHANGELOG section. Ships the four maintenance fixes already
on develop:
* fix(cache): invalidate signal cache after npm-registry
signal-shape changes (SCHEMA_VERSION 1 -> 2)
* fix(policy): suppress dist-tag anomaly when resolved version
is latest
* fix(npm-registry): only fire dist-tag anomaly across
major-version boundary
* feat(policy): ship default scripts.allow for known
native-binary packages
Together these cut the residual 21 false-positive blocks in the
real-world 1276-package scan that motivated v0.1.1 down to the
true positives only.
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.
Four-commit maintenance release driven by a real-world 1276-package scan after v0.1.1.
Fixes
fix(cache)(dc42f48) \u2014 bump signal-cacheSCHEMA_VERSION1 -> 2 so caches written by v0.1.0/0.1.1 are auto-invalidated under 0.1.2. Closes the 'I upgraded but the noise didn't go away' loop.fix(policy)(06bc885) \u2014 suppressDistTagAnomalywhen resolved version IS latest (attr-accept@2.2.5,get-intrinsic@1.3.0).fix(npm-registry)(f9fee0f) \u2014 only fireDistTagAnomalyacross major boundaries; same-major LTS holds (@storybook/*@8.6.x) no longer block.feat(policy)(94c468a) \u2014 defaultscripts.allowcoversbcrypt cypress electron esbuild fsevents msw node-gyp node-pre-gyp playwright puppeteer sharp.Release commit
chore(release): v0.1.2(91c2f1a) \u2014 workspace + path-version bump, CHANGELOG.Validation
cargo test --workspacegreen (191 tests across crates)cargo clippy --workspace --all-targets -- -D warningsclean