Skip to content

Install gate: remove recency gate, keep publish-date provenance#119

Open
juangaitanv wants to merge 7 commits into
mainfrom
quick-fixes
Open

Install gate: remove recency gate, keep publish-date provenance#119
juangaitanv wants to merge 7 commits into
mainfrom
quick-fixes

Conversation

@juangaitanv

Copy link
Copy Markdown
Contributor

Summary

Removes the recency gate from the install wrappers (corgea pip|npm|yarn|pnpm|uv). A freshly-published package version no longer blocks an install; the vuln-api verdict is now the gate's single block condition. Each resolved package still shows its publish time for provenance — that line is kept, just made non-blocking.

Motivated by the recency gate causing friction on legitimate fresh releases (e.g. a 1-day-old axios) and by the -t/--threshold flag's misplacement footgun.

Behavior change

Before — fresh pin blocked:

⚠ axios → axios@1.18.1  published 1d 17h ago at 2026-06-22 18:28:17 UTC (within threshold)
Refusing to run install. Pass --no-fail to proceed anyway.

After — installs, publish time shown as neutral provenance:

Pre-checking `pnpm add axios`
  1 ok, 0 vulnerable, 0 unverifiable, 0 skipped, 0 errors
  ✓ axios → axios@1.18.1  published 1d 18h ago at 2026-06-22 18:28:17 UTC

What was removed / kept

Removed Kept
Recency block (BlockReason::RecencyOnly) vuln-api verdict (now the only block)
-t/--threshold flag, parse_threshold format_duration (age display)
--no-fail flag (only demoted recency) --force, --json
is_recent, recent_count, threshold fields published_at fetch + age
Header (threshold …), (within threshold), the -r "not recency-checked" note published <age> ago at <UTC ts> on every named target
JSON threshold_seconds, per-result recent; verdict_mode "recency-only" verdict_mode "none"

Commits in this PR

  • dca9621 Install gate: remove recency gate, keep publish-date provenance (the change above)
  • 45855c9 Install gate: explain misplaced escape flag in refusal hint (--force typed after the verb)
  • dad4e0f, 218d800 pyproject.toml project config (pre-existing on the branch)

Test plan

  • Recency tests repurposed, not just deleted: fresh pins now assert they install; the two npm gating tests assert the gate engaged via its Pre-checking header / registry hit instead of a recency refusal.
  • Full suite green (17 suites), clippy strict + rustfmt clean (pre-commit hook).
  • Manual: corgea pnpm add axios against a throwaway project — proceeds, prints the publish-date line, no refusal.

🤖 Draft — review before merge.

A --force/--no-fail typed after the install verb is swept into clap's
trailing-var-arg and forwarded to the package manager, so the gate never
sees it and the block stands. Users (reasonably) read 'Pass --force' and
retry with the flag in the same wrong slot.

print_escape_hint now detects an accepted escape flag sitting in the
forwarded args and prints a note naming the package manager that received
it, plus the corrected invocation with the flag between manager and verb
(corgea uv --force add requests). Covered by a pip integration test.
The recency block (a version published within --threshold blocks the
install) is gone, along with the -t/--threshold flag, the --no-fail
demotion flag, parse_threshold, and the RecencyOnly block reason. The
vuln-api verdict is now the gate's single block condition.

Every resolved package still shows its publish time for provenance
('published <age> ago at <UTC timestamp>') — now a neutral line on every
named target rather than a within-threshold warning. JSON drops
threshold_seconds and the per-result 'recent' status/count; verdict_mode
'recency-only' becomes 'none'.

Tests repurposed, not just deleted: fresh pins now assert they install;
the two npm gating tests assert the gate engaged via its Pre-checking
header / registry hit instead of a recency refusal. SKILL.md updated.
@juangaitanv juangaitanv marked this pull request as ready for review June 24, 2026 13:44
Comment thread src/precheck/render.rs
Comment thread src/precheck/verdict.rs
Comment thread src/main.rs
When pip backtracks a named target to a version the CLI never resolved,
apply_verdicts adopts the installed version on the named row but its
publish date / age belonged to the CLI-resolved version. Render then
printed e.g. `flask@3.0.2 published … at <date-for-3.0.3>` — a wrong date
presented as provenance, the one thing the recency-removal PR keeps.

Make `TargetOutcome::Resolved.age` an `Option<Duration>`, set it to `None`
in the backtrack collapse (rather than re-fetch on the gate's critical
path), and have render omit the publish line (text) / null `published_at`
+ `age_seconds` (JSON) when it's absent. Tests assert the backtracked row
carries no stale date and an exact match keeps its provenance.

Also clean the stale "recency" wording the gate removal left behind:
wrapper --help strings, SKILL.md, and the locked-install comments.
Bring back the recency block removed in dca9621, this time driven by
~/.corgea/config.toml instead of the -t/--threshold and --no-fail flags.
Two new fields, both serde-defaulted so an upgraded config inherits the
gate rather than silently disabling it:

  recency_gate = true           (CORGEA_RECENCY_GATE)
  recency_threshold_days = 14   (CORGEA_RECENCY_THRESHOLD_DAYS)

block_reason gains BlockReason::Recency as a softer second gate: a clean
named target published within the window blocks only when no vulnerable/
unverifiable finding already did. Unknown publish dates (pip backtracking,
age = None) never block, so the stale-provenance bug 1496887 fixed stays
fixed. The refusal names each fresh package and points at the config
toggle; --force still bypasses. JSON adds top-level recency_threshold_days
(null when off) to pair with each result's age_seconds.

Tests: a config unit test pins the opt-out default + back-compat; five
e2e tests cover block-on, --force bypass, threshold-0 plumbing, old-pin
allowed, and gate-off installs. The shared test harness pins the gate off
so existing gate tests stay 'every block is the verdict's doing'.

Docs: SKILL.md and README document the gate, window, and toggle.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant