Skip to content

v1.58.0.0 fix wave: 8 community bugs (4 security guards failing open)#1911

Open
garrytan wants to merge 12 commits into
mainfrom
garrytan/triage-open-issues
Open

v1.58.0.0 fix wave: 8 community bugs (4 security guards failing open)#1911
garrytan wants to merge 12 commits into
mainfrom
garrytan/triage-open-issues

Conversation

@garrytan
Copy link
Copy Markdown
Owner

@garrytan garrytan commented Jun 8, 2026

Fix wave: 8 community-filed bugs (4 security guards failing open)

Triage of the open-issue backlog surfaced an ASAP cluster — guards that reported success while doing nothing. This wave fixes 8, each as its own bisect commit with a regression test that fails on main and passes after.

Five scarier-titled issues turned out already-fixed on main and were closed separately (#1734, #1781, #1576, #1769, #1803).

Fixes

# Area What was broken
#1899 /ship adversarial review Subagent reasoning "like an attacker" over the repo's own attack-payload fixtures tripped Anthropic's usage-policy block, denying the review. Now carries authorized-defensive-testing framing + reads fixtures in summary mode. Fixed at the prompt source (scripts/resolvers/review.ts).
#1868 redaction openai.key (HIGH/block) missed modern sk-proj-/sk-svcacct-/sk-admin- keys (base64url bodies) — a HIGH credential failing open through /spec, /ship, /cso, /document-*. Explicit bare-vs-prefixed alternation + FP guards.
#1824 redaction Malformed --max-bytes parsed to NaN, silently disabling the fail-closed oversize guard. CLI rejects non-integers; engine backstops with the default cap.
#1745 learnings Cross-project trust gate was a denylist (trusted === false) despite being documented as an allowlist, leaking rows missing the field. Flipped to trusted !== true.
#1839 safety One-way-door classifier omitted password from the rotate pattern, so "rotate the database password" classified as reversible/auto-approvable.
#1810 review army gstack-diff-scope matched only *.ts|*.js; .mjs/.cjs/.mts/.cts-only PRs skipped the backend reviewer.
#1879 brain-cache loadMeta crashed (TypeError) on a valid _meta.json missing last_refresh. Shape-guard + map normalization; missing identity forces a safe rebuild.
#1871 safety skills guard/freeze/careful hooks used ${CLAUDE_SKILL_DIR}, unset on Claude Code 2.1.162, erroring on every Edit/Write. Anchored to the installed checkout path.

Reviews run before implementation

Scoped out

#1882 (skills hardcode ~/.claude/skills/gstack/, breaking non-gstack install dirs) is filed as the top TODOS.md item. It proved to be a host-config/preamble change touching all 52 skills, distinct from the #1871 hook fix it was originally paired with.

Verification

  • bun test green (free suite, includes all 8 regression tests).
  • Each fix's regression test fails on main, passes after.
  • bun run slop:diff: zero findings in changed source files.

🤖 Generated with Claude Code

garrytan and others added 12 commits June 7, 2026 22:46
…n own security fixtures (#1899)

The Claude adversarial subagent in /review and /ship was told to "think like an
attacker" over the full diff. When the diff includes the repo's own security
regression fixtures (real attack payloads, by design), reasoning adversarially
over that material triggered Anthropic's real-time usage-policy safeguards and
the subagent call was denied — blocking the review.

Fix at the prompt's source of truth (scripts/resolvers/review.ts {{ADVERSARIAL_STEP}}):
- Authorized-defensive-testing framing: declares this is the maintainer's own repo
  and that attack-pattern strings inside test/fixture paths are the project's own
  regression corpus to analyze, not material to expand on.
- Fixture summary-mode diff: full content for non-fixture source, --stat/--name-status
  for test/fixture files, so raw exploit bytes aren't fed into adversarial reasoning.
  The subagent must state fixtures were reviewed in summary mode (no silent coverage cut).

Reported by @bmajewski. Regenerated review/SKILL.md + ship/sections/adversarial.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…#1868)

openai.key (HIGH/block) used /\b(sk-(?:proj-)?[A-Za-z0-9]{32,})\b/, which stops
at the first - or _ in the body. Modern OpenAI project/service-account/admin keys
use base64url bodies containing - and _, so they never reached the 32-char run and
produced ZERO findings — a HIGH credential failing open through /spec, /ship, /cso,
and /document-*.

Replace with explicit alternation, bare vs prefixed (not a globally-optional prefix,
which would match malformed sk--... or separator-less sk-projabc...):
  sk-{proj,svcacct,admin}- + [A-Za-z0-9_-]{20,}  |  sk-[A-Za-z0-9]{32,} (legacy)

Tests: the three previously-missed shapes now block; FP guards pin that hyphenated
prose and malformed sk- strings do NOT match (HIGH tier blocks, so calibration matters).

Reported by @jbetala7.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ng the size guard (#1824)

The oversize check is designed to fail CLOSED, but a malformed --max-bytes turned
it fail-OPEN. bin/gstack-redact did parseInt(maxBytes,10) and passed it straight
through; parseInt("foo") is NaN. The engine guarded with `opts.maxBytes ?? DEFAULT`,
and ?? does not catch NaN, so `byteLen > NaN` was always false and the fail-closed
block never fired. A negative value made `byteLen > -5` always true, blocking
everything.

Two layers:
- bin/gstack-redact validates the RAW string (parseInt accepts "123abc"->123,
  "1.5"->1): require /^\d+$/ and > 0, else exit 1 with a clear message.
- lib/redact-engine.ts hardens the fallback to Number.isFinite && > 0 else the
  default cap — a guardrail so the engine never silently runs uncapped even if a
  bad value reaches it directly.

Tests: NaN and negative both fall back to the default cap (oversize still blocks);
CLI rejects garbage/negative with exit 1.

Reported by @jbetala7.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ist (#1745)

gstack-learnings-search --cross-project is documented as an allowlist — foreign
learnings load only when user-stated/trusted, to stop one project's AI-generated
learnings from injecting into another project's reviews. It was implemented as a
denylist: `if (isCrossProject && e.trusted === false) continue`. Any row where
`trusted` is missing/undefined (legacy rows from before the field existed,
hand-edited rows, rows from other tools) passed `undefined === false` → false →
admitted. Those rows leaked across projects.

Flip to `e.trusted !== true`. Test: a foreign row with no `trusted` field is now
excluded (true still included, false still excluded).

Reported by @jbetala7.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…1839)

scripts/one-way-doors.ts is the secondary safety net for ad-hoc AskUserQuestion
ids with no registry entry; a false negative auto-approves a destructive op. The
revoke and reset credential patterns both include `password`, but the rotate
pattern omitted it, so the most common phrasing ("rotate the database password")
classified as a reversible two-way question.

Add `password` to the rotate alternation so all three verbs are parallel. New test
covers rotate+password, the revoke/reset/rotate parallel, and rotate's other nouns.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
#1810)

gstack-diff-scope backend detection matched only *.ts|*.js. Modern Node ships
backend code as ESM (.mjs) / CommonJS (.cjs) and explicit-module TS (.mts/.cts);
none matched any category, so a PR touching only those files reported no backend
scope and the Review Army skipped the backend reviewer.

Add the four module extensions to the backend case. Test covers all four.

Reported by @jbetala7.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…shing (#1879)

loadMeta returned the parsed JSON verbatim. A valid JSON file that lacked the
last_refresh map made three consumers (isStale, cmdInvalidate, refreshEntity)
throw a TypeError dereferencing meta.last_refresh — the sibling last_attempt was
already guarded, last_refresh wasn't.

Fix in loadMeta:
- Shape-guard: JSON.parse can return null/array/string/number; non-object → fresh meta.
- Normalize ONLY the dereferenced maps (last_refresh, last_attempt).
- Deliberately do NOT default schema_version/endpoint_hash. Leaving them absent
  makes schemaVersionMismatch()/endpointSwitched() force a rebuild (missing
  identity = mismatch = safe); defaulting them would suppress cache invalidation
  and trust a stale file of unknown provenance.

Tests: missing last_refresh no longer throws; null/array/primitive treated as cold;
missing schema_version forces rebuild instead of a trusted warm hit.

Reported by @jbetala7.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…C 2.1.162 (#1871)

The PreToolUse frontmatter hooks for guard, freeze, and careful invoked
`bash ${CLAUDE_SKILL_DIR}/.../check-*.sh`. Claude Code 2.1.162 no longer populates
${CLAUDE_SKILL_DIR} in the skill-hook execution env, so it expanded to empty and
every Edit/Write/Bash ran `bash /...` and errored — breaking the safety skills
entirely.

Frontmatter hooks run before any skill-body bash, so no runtime-resolved variable
can fix this; the command must be a path that's valid at hook time. Anchor to the
installed checkout: $HOME/.claude/skills/gstack/{careful,freeze}/bin/check-*.sh,
where the scripts actually live. ($HOME is expanded by the hook shell.)

Reported by @omariani-howdy. Regenerated the three SKILL.md from templates.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CHANGELOG entry for the 8-fix safety wave (#1899, #1868, #1824, #1745, #1839,
#1810, #1879, #1871). VERSION + package.json to 1.58.0.0 (MINOR — coordinated
multi-file safety fixes on top of main's 1.57.3.0). #1882 filed as the top
TODOS.md item (scoped out of this wave per decision; host-config change touching
all 52 skills, distinct from the #1871 hook fix).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…block

The #1745 trust-gate fix added an explanatory comment containing backticks
(`=== false`) and the JS block is a double-quoted `bun -e "..."` bash string, so
bash command-substituted the backtick contents on every cross-project search —
polluting stderr with "command not found" and leaving a latent shell-injection /
source-corruption surface in a security gate. Caught by the wave's own adversarial
review (#1899 framing working as intended). Reworded the comments to avoid backticks
and dollar-paren entirely; the gate logic is unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…-title line)

The three ship golden fixtures were stale: main's v1.57.3.0 added the always-loaded
PR-title invariant to ship/SKILL.md but did not regenerate the goldens (the golden
regression test fails on main too), and the codex golden still carried an unresolved
${ctx.paths.binDir} token. Regenerated from the current generated ship skills, which
also picks up this wave's #1899 adversarial-prompt framing (inlined for codex/factory).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@trunk-io
Copy link
Copy Markdown

trunk-io Bot commented Jun 8, 2026

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

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