* fix: V-001 security vulnerability
Automated security fix generated by OrbisAI Security
* fix: sanitize subprocess call in runner.py
The runner
* fix: address PR review comments on V-001 allowlist and test coverage
Remove dangerous interpreters (python, python3, node, curl, wget) from
ALLOWED_SETUP_EXECUTABLES — they can execute arbitrary code via argument
flags and are not needed for sandbox setup. Rewrite test_invariant_runner
to call _setup_sandbox directly instead of spawning runner.py as a
subprocess (which had no __main__ entrypoint and never exercised the fix).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Three defense-in-depth fixes around untrusted input flowing to subprocess execution:
1. **Control-pane HTTP server (scripts/lib/control-pane/server.js)**
The local control-pane API binds to 127.0.0.1 but had no Host or Origin
validation, so a DNS-rebinding attack from a malicious website could pivot
into the loopback endpoints — including POST /api/actions/:id, which spawns
'cargo run -- graph ...' with caller-supplied query strings. Add a hostname
allowlist (loopback variants plus the explicitly configured --host) and
reject mismatched Host (421) or non-loopback Origin (403) before any route
handler runs.
2. **OpenCode git-summary tool (.opencode/tools/git-summary.ts)**
The tool was building 'git diff ${baseBranch}...HEAD --stat' with execSync
and a raw model-supplied baseBranch string. Switch run() to execFileSync
with an args array (no shell), validate baseBranch against a conservative
git-ref allowlist (rejects shell metacharacters, leading -, embedded ..),
and clamp the depth arg to a small positive integer before interpolating
into 'git log --oneline -<N>'.
3. **Reusable test workflow (.github/workflows/reusable-test.yml)**
The 'Install dependencies' step interpolated ${{ inputs.package-manager }}
directly into a bash 'case' and into an echo, so a downstream caller that
forwarded attacker-controllable input could inject into the runner. Move
the input into a PACKAGE_MANAGER env var and reference $PACKAGE_MANAGER
inside the script per the GitHub script-injection guidance.
Detected by Aeon + semgrep p/security-audit (host check via threat-model
manual-review axis; git-summary via detect-child-process; workflow via
run-shell-injection).
Verification: node tests/run-all.js — 2686/2687 pre-existing tests pass; the
one failure (observe.sh legacy output fallback) reproduces on main without
this branch applied. Added 2 new control-pane tests covering the allowlist
classifier and the DNS-rebinding-gate behavior end-to-end.
---
Filed by [Aeon](https://github.com/aaronjmars/aeon-aaron).
Co-authored-by: aeonframework <aeon@aaronjmars.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).
runHooks() returned the unmodified raw stdin (the PreToolUse/PostToolUse
input event) on stdout whenever no sub-hook produced additionalContext.
Claude Code parses a hook's stdout as JSON and validates it against the
hook-output schema, so echoing the input object
({session_id, hook_event_name, tool_name, tool_input, ...}) fails with
"Hook JSON output validation failed — (root): Invalid input" on nearly
every Bash command.
Track whether a sub-hook deliberately set stdout (string / {stdout}, e.g.
GateGuard) via a rawModified flag and emit '' in the pass-through case
instead of the echoed input. Preserves GateGuard pass-through and
block-no-verify's exit-2 blocking.
Update the three dispatcher tests that codified the buggy echo behavior to
expect empty stdout, and add a regression test for a plain pass-through
command.
Fixes#2239
Co-authored-by: WOZCODE <contact@withwoz.com>
On Windows both cfg(windows) and cfg(not(unix)) evaluate true, so the sync taskkill kill_process and the async taskkill kill_process both compiled in and collided (E0428). Call sites are synchronous and never await it (passed as a fn pointer to enforce_session_heartbeats_with, and called as kill_process(pid)? in stop_session_recorded), so remove the stray async cfg(not(unix)) definition. The sync cfg(windows) version already handles termination via taskkill /T /F.
* docs: add official-sources security warning to README
Add a GFM [!WARNING] alert near the top of README.md identifying
github.com/affaan-m/ECC and the ecc-universal / ecc-agentshield npm
packages as the only verified distribution channels, and warning users
that third-party re-uploads may contain malware.
Closes#2242
* Update README.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
---------
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Affaan Mustafa <affaan.mustafa09@gmail.com>
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
- suggest-compact hook now reads the latest usage record from the session
transcript and suggests /compact at a window-scaled token threshold
(160k/200k window, 250k/1M window; COMPACT_CONTEXT_THRESHOLD and
COMPACT_CONTEXT_INTERVAL overridable), re-firing per 60k-token growth
bucket; tool-call count stays as the secondary signal (#2155)
- Codex repo marketplace now points at ./plugins/ecc instead of ./ — Codex
never discovers plugins whose local marketplace source.path is the
marketplace root (verified on Codex CLI 0.137.0); plugins/ecc is a thin
folder referencing root skills/.mcp.json per maintainer direction on
#2097; docs flag plugin mode as experimental with the upstream blocker
openai/codex#26037 linked (#2128)
- README badges for installs/stars/forks now use shields endpoint badges
backed by api.ecc.tools (live install count 3,712 vs the stale static
150), which also eliminates shields' 'Unable to select next GitHub token
from pool' render in the stars badge
Closes#2155Closes#2128
- competitive-platform-analysis: add ## Examples section per ECC
guidelines (8-axis taxonomy walkthrough + pre-filter scoring matrix)
- competitive-report-structure: clarify dimension 9 poles are client-
specific (e.g., Memorability/Hireability) not hard-coded names
- brand-discovery: fix terminal state — set inProgressModule to null
after 90_SYNTHESIS.md is complete to prevent misleading resumption
All fixes mirrored to .agents/ copies.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v2.0.0-rc.1 shipped in April 2026 with 261 skills; the four new skills added
in this PR bring the count to 265 only in v2.0.0+. Retroactively updating the
rc.1 entry rewrote past release facts — restore the accurate historical count.
Addresses cubic-dev-ai review finding (README.md:137).
Adds four community skills covering brand identity discovery and a
three-skill competitive benchmarking pipeline.
**brand-discovery** — Adaptive multi-session brand identity interview
spanning 8 modules (purpose, positioning, audience, personality, voice,
narrative, founder-brand tension, synthesis). Uses laddering, 5 Whys,
and projective techniques. State persisted to disk via state.json so
sessions resume across conversations without losing elicited knowledge.
Frameworks: Sinek, Dunford, Baker, Enns, Kapferer, Aaker, Neumeier,
Mark & Pearson, Lencioni. Includes 8 module output templates in
references/.
**competitive-platform-analysis** — Scopes and tiers a competitor set
before benchmarking begins. Categorizes candidates along 8 generic
creative-industry axes (positioning stance, specialization, size/model,
engagement format, distinctiveness posture, evidence model, brand
strength, market/reach) into Direct / Adjacent / Aspirational tiers.
Includes a pre-filter scoring matrix. First step in the pipeline.
**benchmark-methodology** — Scores each competitor across 9 weighted
dimensions (positioning 18%, brand voice 15%, visual craft 15%, offer
packaging 12%, evidence 12%, enterprise-readiness 10%, thought
leadership 8%, pricing 5%, client's strategic tension 5%) with explicit
1–5 rubrics and bias controls. Produces one profile card per competitor.
**competitive-report-structure** — Assembles scored cards into a
decision-grade report: executive summary, landscape map, competitor
tiers, heatmap matrix, deep dives, white-space and threats, strategic
recommendations, sources appendix.
brand-discovery complements brand-voice (ECC): brand-voice extracts a
style profile from existing source material; brand-discovery elicits
identity from scratch through structured interviews when no prior
material exists.
A competitive set scoped without the client's positioning brief is
noise, not intelligence — each skill enforces this by requiring the
brief before proceeding. The 9-dimension scoring framework deliberately
reports the client's strategic tension as two separate poles (never
averaged) because the gap between them is the strategic finding.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The store write was unconditional, persisting work items even during dry
runs. Move it inside the !dryRun block alongside editIssue and initialize
snapshot to null beforehand so results.push still receives snapshot: null
for dry runs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
applyPublish was forcing review='approved' for any state that wasn't
'changes-requested', bypassing policy.review.required entirely. Add a
guard that throws before buildIssueStateFromAction when review approval
is required but not yet granted.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove circular validation-status check in applyValidate that prevented
fresh claims (validation='pending') from ever reaching 'passed'
- Add staleCoordinationLabels helper to compute coordination:* labels to
remove on state transitions; replaces hardcoded removeLabels:[] across
all six editIssue call sites
- Fix duplicate label writes in applySync: syncIssueLabels already calls
editIssue for labels, so the follow-up editIssue now only updates body
- Skip acquireLock finding: store.acquireLock does not exist; comment
updated to explain why the fix was not applied
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
actions.js:
- Add assertValidRepo/assertValidIssueNumber guards at the top of all
action handlers (applyClaim, applySync, applyValidate, applyPublish,
applyReview, applyDecompose, applyUnblock) for fast-fail validation
- applyValidate: fix status transition — set 'validated' unconditionally
when ok=true instead of preserving 'blocked' (was inconsistent with
projectState becoming 'ready')
gh-api.js:
- runGh: preserve GITHUB_TOKEN by default; only delete when caller
explicitly sets options.stripGithubToken=true (was deleting by
default, breaking CI)
parsing.js:
- extractCoordinationState: throw SyntaxError on malformed JSON instead
of silently returning null — lets callers distinguish bad JSON from
absent marker
- normalizeBodyForComparison: fix regex to match JSON-quoted form
"lastSyncAt": ... instead of bare lastSyncAt: ...
policy.js:
- loadPolicy: validate that parsed JSON is a plain object before
spreading; coerce nested fields (labels, review, validation,
branchModel, project, fieldNames) to objects before merging
state.js:
- assertIssueClaimable: block re-claim on status alone (not status AND
owner) to prevent {status:'claimed', owner:null} bypass; use
state.owner || 'unknown' in error message
- getCoordinationState: catch SyntaxError from extractCoordinationState,
log warning to stderr, fall back to default state
tests/lib:
- Update malformed-JSON test to expect SyntaxError throw instead of null
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(deps): bump crossterm from 0.28.1 to 0.29.0 in /ecc2
Bumps [crossterm](https://github.com/crossterm-rs/crossterm) from 0.28.1 to 0.29.0.
- [Release notes](https://github.com/crossterm-rs/crossterm/releases)
- [Changelog](https://github.com/crossterm-rs/crossterm/blob/master/CHANGELOG.md)
- [Commits](https://github.com/crossterm-rs/crossterm/commits/0.29)
---
updated-dependencies:
- dependency-name: crossterm
dependency-version: 0.29.0
dependency-type: direct:production
update-type: version-update:semver-minor
...
Signed-off-by: dependabot[bot] <support@github.com>
* fix(ecc2): switch ratatui feature to crossterm_0_29
Keep a single crossterm version in the tree after the 0.29 bump;
with crossterm_0_28 the lockfile carried both 0.28.1 and 0.29.0.
---------
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Affaan Mustafa <me@affaanmustafa.com>
* fix(assets): replace hero brand mark with website coral circuit mark
The top-left mark in the hero banner was the assets/ecc-icon.svg double-E
lettermark, not the actual brand logo. Swap in the coral vector circuit
mark from the ECC-website header (src/styles/brandMarks.ts), keeping the
~70px footprint, the soft coral glow, and every other element identical.
PNG re-rendered at 2400x1350 via sharp with palette compression.
* docs: sync skill count to 262 across catalog surfaces
catalog:check was failing on main after config-gc (#2216) landed without
a count bump. Ran npm run catalog:sync.
#2209 bumped ureq to 3.x but the AgentBuilder-based webhook sender
was not ported (branch update raced the merge). ureq 3 replaces
AgentBuilder with Agent::config_builder(); timeouts are Option-wrapped
and status() returns http::StatusCode.
Recreates the v1.10 hero banner design (sourced from commit 602894ef)
that PR #2225 replaced with a plain HTML header:
- Wordmark and breadcrumb now read ECC / affaan-m/ECC
- Version badge reads v2.0.0 · Jun 2026, eyebrow updated to V2.0
- Top-left mark is the actual assets/ecc-icon.svg lettermark (amber E,
coral CC) instead of a generic coral square
- Catalog columns refreshed with live counts (261 skills, 64 agents,
84 commands, 409 catalog) and real item names from the repo
- Harness pills updated to the current README list (Claude Code, Codex,
Cursor, OpenCode, Gemini, Zed, Copilot)
- SVG source committed as assets/hero.svg so future edits never need
image archaeology; rendered to PNG at 2400x1350 via sharp
README hero line restored to the markdown image; badges, sponsor table,
and guide cards from #2225 kept intact.
* docs(zh-CN): translate ecc-guide and parallel-execution-optimizer skills
Adds Simplified Chinese translations for two untranslated skills,
following the existing docs/zh-CN/skills/ conventions (frontmatter
name/origin preserved, code blocks and output templates kept in
English, prose fully translated).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* docs(zh-CN): polish two phrasings per review
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>