* 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.