Use no-follow file descriptors for legacy Codex snapshots, verification, restoration, and marker cleanup. Quarantine candidate removals and verify inode identity before deletion.
Carry the lifecycle runner as a verified artifact so privileged release workflows never dynamically check out and execute an output-selected revision.
main has been red since the Itô skill series landed. Two independent
problems, both in test files rather than shipped behavior:
- tests/ci/ito-inference-skill.test.js asserted a stale copy of the
capability:ito-compute description. #2706 added device revocation to
the lifecycle and updated manifests/install-components.json, but this
expectation was not updated with it. The manifest is the shipped
artifact, so the test expectation is what was wrong.
- three ito test files matched YAML frontmatter indentation with two
literal spaces inside a regex literal, which trips no-regex-spaces.
Replaced with an explicit ` {2}` quantifier, which matches identically.
The basket-compare occurrence was not visible in CI: npm run lint is
`eslint . && markdownlint ...`, so ESLint reported only the first file
and stopped. Fixing only what CI printed would have left main red on the
next run. The markdownlint half of that chain had therefore never
executed; it passes.
Verified on this branch: full suite 3707/3707, repo-wide ESLint clean,
and markdownlint clean under the exact CI glob.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* fix: ship new Ito skills through install manifests
* ci: audit shipped dependencies separately from tooling
* test(release): pass previous version to heading helper
Add a local-first, cross-harness memory vault with CLI and MCP surfaces, bounded search and storage, harness-scoped visibility, setup guidance, and comprehensive tests.
Expose the canonical Itō CLI's pinned sixtytwo node-qualification path through ECC with double opt-in, explicit node/config gates, credential isolation, and no new MCP or execution authority.
Validated across the full Linux, macOS, and Windows Node/package-manager matrix, hosted coverage, CodeQL, security, lint, and focused bridge tests.
devin-ai-integration[bot]GitHubDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>affaanDevin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: prevent IOC scanner false positives on hook filenames and scan .cursor configs
The supply-chain IOC scanner matched CRITICAL_TEXT_INDICATORS with plain
substring search, so legitimate hook filenames that merely end with a known
payload name (e.g. the stock Cursor hook before-shell-execution.js vs the
payload execution.js) were flagged as CRITICAL. Indicator matching now
requires a non-filename character before the match.
Also add .cursor/ to the special config paths so Cursor hooks.json files
(a known persistence vector already listed in PERSISTENCE_FILENAMES) are
actually inspected in normal checkouts - previously they were only scanned
by accident when the repo path happened to contain /.claude/.
* test: cover underscore-prefixed filenames in IOC boundary suppression
Make explicit that '_' is treated as a filename word character, so
snake_case hook names like post_execution.js are intentionally not
flagged by the execution.js indicator (real payload references appear
after '/', quotes, or whitespace).
ROOT CAUSE: hooks load plugin-hook-bootstrap.js via
`node -e "...; process.argv.splice(1,0,s); require(s)"`. On Node 21+,
require.main is `undefined` under --eval, so the `if (require.main === module)`
guard was false and main() never ran — every plugin hook silently no-op'd
(e.g. the MCP-health PreToolUse hook stopped blocking). CI (Node 18/20) hid
this; it only surfaces on Node 21+. Fix: also run main() when require.main is
undefined (the eval-bootstrap case), while staying dormant on real imports.
Also clears pre-existing main debt the full local suite enforces:
- catalog:sync — README/docs agent+skill counts drifted after recent merges
- tests/ci/supply-chain-watch-workflow: update checkout SHA to the merged v6.0.3 (#2183)
- markdownlint + check-unicode-safety --write across docs/skills
Suite: 2683/2683 green under Node v25; lint + unicode clean.
Co-authored-by: ECC Test <ecc@example.test>
* test: guard broken-symlink tests so the suite passes on Windows
Four test cases create a dangling symlink with fs.symlinkSync() to exercise
statSync catch branches, but did not guard for platforms where symlink
creation is not permitted. On Windows without Developer Mode / admin rights,
fs.symlinkSync throws EPERM, so these tests fail and `npm test` is red:
- tests/ci/validators.test.js (Round 73, validate-commands skill entry)
- tests/lib/session-manager.test.js (Round 83, getAllSessions)
- tests/lib/session-manager.test.js (Round 84, getSessionById)
- tests/lib/utils.test.js (Round 84, findFiles)
Wrap each symlinkSync in try/catch and skip cleanly on failure, mirroring the
existing convention already used in this repo (validators.test.js Round 57 and
hooks/config-protection.test.js). On Linux/macOS and admin Windows the symlink
still succeeds and the tests run unchanged; only the unsupported-symlink path
now skips instead of failing.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test: only skip symlink tests on EPERM/EACCES, rethrow other errors
Address CodeRabbit review: the catch blocks swallowed every error, which could
mask a real test/setup failure as a false skip. Inspect err.code and only take
the skip path for EPERM/EACCES (symlink creation blocked, e.g. Windows without
Developer Mode); rethrow anything else so genuine failures still surface.
Per the repo coding guideline: never silently swallow errors.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds dynamic workflow/team orchestration skills, the content pack, and control-pane work-item/Kanban state DB support. Includes reviewer hardening for state-db CLI validation, optional state DB failure handling, and mergeStateStatus projection.
Three test changes in response to the round-1 review:
1. **Add quoted-write-all coverage** (cubic P0 follow-up).
Two new cases assert the regex now matches the double-quoted and
single-quoted YAML forms of `permissions: "write-all"`:
- `rejects double-quoted permissions: "write-all"`
- `rejects single-quoted permissions: 'write-all'`
Both fixtures trigger only the persist-credentials gate, so they
exercise the WRITE_ALL_PATTERN OR-clause in isolation.
2. **Add expression+ref dedup coverage** (greptile P2 follow-up).
`emits a single violation when both expressionPattern and refPattern
match the same step` — uses `refs/pull/${{ … head.sha }}/merge` as
the fixture (which matches both patterns) and counts ERROR lines for
the `pull_request_target` rule, asserting exactly one. Re-introducing
the duplicate-push bug would re-fail this test immediately.
3. **Drop the `npm ci without --ignore-scripts under write-all` test**
(greptile P2). That test happened to pass under the previous
`--ignore-scripts` regex, but `UNSAFE_INSTALL_PATTERNS` (added in
`f7035b56`) fires unconditionally for every workflow regardless of
permissions. So the test was exercising a pre-existing code path
that has nothing to do with WRITE_ALL_PATTERN. Reviewer flagged this
could mislead future contributors into thinking lifecycle-script
enforcement is gated on write permissions.
Replaced by the surrounding `rejects checkout credential persistence
in workflows with permissions: write-all` test (already present) and
the new quoted-form tests above, which all exercise the actual
persist-credentials gate that the WRITE_ALL_PATTERN clause newly
activates.
Test count: 22 → 24 (added 3 new, dropped 1). All green; `yarn lint`
clean.
The cohort comment above the write-all block was also tightened to
explicitly note that "the lifecycle-script gate already fires
unconditionally for every workflow" so the next reader sees the
distinction up front.
The `pull_request_target` rule's `expressionPattern` matches only
the canonical `github.event.pull_request.head.{ref,sha,repo.full_name}`
interpolations. It does not match the second canonical form of
the same exploit — fetching `refs/pull/<N>/{head,merge}` directly:
- uses: actions/checkout@v4
with:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
The merge-ref variant is what GitHub's own security guidance calls
out as the highest-severity privilege-escalation pattern under
`pull_request_target`: it materialises the PR's merge commit
(attacker code spliced with base), executes inside a workflow that
has full repo-scoped tokens, and gives the attacker the chance to
exfiltrate secrets or push to default branches. `refs/pull/N/head`
is functionally equivalent — same source, same trust boundary.
Reproduced on `main` before this commit:
$ cat /tmp/bad.yml
name: bad
on: { pull_request_target: { types: [opened] } }
permissions: { contents: read }
jobs:
do:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
persist-credentials: false
- run: npm ci --ignore-scripts
$ ECC_WORKFLOWS_DIR=/tmp node scripts/ci/validate-workflow-security.js
Validated workflow security for 1 workflow files
$ echo $?
0
Expected: violation flagging the refs/pull checkout under pull_request_target.
Actual: passes silently.
Fix: add a `refPattern` to the `pull_request_target` rule:
/^\s*ref:\s*['"]?[^'"\n]*refs\/(?:remotes\/)?pull\/[^'"\n\s]+/m
and apply it per checkout step inside the existing
event-gated loop. The pattern matches the ref VALUE so it catches
all interpolation shapes — `refs/pull/123/head`,
`refs/pull/${{ github.event.pull_request.number }}/merge`,
`${{ env.FOO }}/refs/pull/N/head` — without enumerating the
possible interpolations themselves.
Scoping: the rule is already gated on the workflow containing
`pull_request_target:`, so non-privileged `pull_request` workflows
that legitimately check out a PR ref are not affected.
After this commit the reproduction above exits 1 with:
ERROR: bad.yml:10 - pull_request_target must not checkout an untrusted pull_request head ref/repository
Three new regression tests in `tests/ci/validate-workflow-security.test.js`:
- rejects pull_request_target + refs/pull/<N>/merge
- rejects pull_request_target + hardcoded refs/pull/<N>/head
- allows pull_request_target with no `with.ref:` (base-ref checkout —
the safe pattern from GitHub's own guidance)
Test count: 17 → 20 in this file; full `yarn test` still green.
Together with the previous commit, this closes the two
independent `validate-workflow-security.js` bypasses I found.
`WRITE_PERMISSION_PATTERN` in `validate-workflow-security.js`
enumerates named GitHub Actions scopes (`contents: write`,
`issues: write`, etc.) to decide whether a workflow needs to:
- disable `persist-credentials` on `actions/checkout`
- pass `--ignore-scripts` to `npm ci`
The pattern misses the top-level shorthand `permissions:
write-all`, which is the strictly broader form — it grants every
named scope write access in a single line. As a result, a
workflow that opts into write-all currently slips both gates.
Reproduced on `main` before this commit:
$ cat /tmp/bad.yml
name: bad
on: [push]
permissions: write-all
jobs:
do:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
$ ECC_WORKFLOWS_DIR=/tmp node scripts/ci/validate-workflow-security.js
Validated workflow security for 1 workflow files
$ echo $?
0
Expected: at least two violations (missing `persist-credentials:
false`, missing `--ignore-scripts`).
Actual: passes silently.
Fix: add a sibling pattern `WRITE_ALL_PATTERN` that matches
`^\s*permissions:\s*write-all\b` and OR it with
`WRITE_PERMISSION_PATTERN` at the single gate. Both top-level
and job-level `permissions:` blocks satisfy the `^\s*` prefix.
After this commit the reproduction above exits 1 with:
ERROR: bad.yml:8 - workflows with write permissions must disable checkout credential persistence
ERROR: bad.yml:9 - workflows with write permissions must install npm dependencies with --ignore-scripts
Three new regression tests in `tests/ci/validate-workflow-security.test.js`:
- rejects write-all + credential-persisting checkout
- rejects write-all + `npm ci` without `--ignore-scripts`
- allows write-all when both gates are satisfied (no over-block)
Test count: 14 → 17 in this file; full `yarn test` still green.
A separate `refs/pull/N/merge` bypass under `pull_request_target`
exists in the same validator and is fixed in the next commit.
Salvages the useful parts of #1897 without generated .caliber state or stale counts.
- adds a deterministic command registry generator and drift check
- commits the current command registry for 75 commands
- validates the rc.1 README catalog summary against live counts
- adds a single Ubuntu Node 20 coverage job instead of running coverage in every matrix cell
Co-authored-by: jodunk <jodunk@users.noreply.github.com>