From c837459e0ab50b6f88fca1313b231a9aacde8960 Mon Sep 17 00:00:00 2001 From: vam Date: Tue, 8 Sep 2026 11:31:06 +0800 Subject: [PATCH 1/8] feat(skills): add agent security hardening --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- AGENTS.md | 4 +- README.md | 4 +- README.zh-CN.md | 2 +- docs/tr/AGENTS.md | 4 +- docs/zh-CN/AGENTS.md | 4 +- docs/zh-CN/README.md | 6 +- manifests/install-modules.json | 1 + package.json | 1 + skills/agent-security-hardening/SKILL.md | 152 +++++++++++++++++++++++ 11 files changed, 168 insertions(+), 14 deletions(-) create mode 100644 skills/agent-security-hardening/SKILL.md diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 3d2ff3e57..03b3f9f85 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -11,7 +11,7 @@ { "name": "ecc", "source": "./", - "description": "Harness-native ECC operator layer - 68 agents, 291 skills, 94 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses", + "description": "Harness-native ECC operator layer - 68 agents, 292 skills, 94 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses", "version": "2.2.1", "author": { "name": "Affaan Mustafa", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 50a41a6f1..5f1e9a391 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "ecc", "version": "2.2.1", - "description": "Harness-native ECC plugin for engineering teams - 68 agents, 291 skills, 94 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses", + "description": "Harness-native ECC plugin for engineering teams - 68 agents, 292 skills, 94 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses", "author": { "name": "Affaan Mustafa", "url": "https://x.com/affaanmustafa" diff --git a/AGENTS.md b/AGENTS.md index 33605f894..085342923 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # Everything Claude Code (ECC) — Agent Instructions -This is a **production-ready AI coding plugin** providing 68 specialized agents, 291 skills, 94 commands, and automated hook workflows for software development. +This is a **production-ready AI coding plugin** providing 68 specialized agents, 292 skills, 94 commands, and automated hook workflows for software development. **Version:** 2.2.1 @@ -154,7 +154,7 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat ``` agents/ — 68 specialized subagents -skills/ — 291 workflow skills and domain knowledge +skills/ — 292 workflow skills and domain knowledge commands/ — 94 slash commands hooks/ — Trigger-based automations rules/ — Always-follow guidelines (common + per-language) diff --git a/README.md b/README.md index 9e4126dd3..5bf27dbee 100644 --- a/README.md +++ b/README.md @@ -136,12 +136,12 @@ Instead of rebuilding that process in every prompt, you install it once and make ECC is MIT-licensed open source. It works best with Claude Code today, has a supported Codex sync path, and provides capability-limited adapters for Cursor, OpenCode, Gemini, Zed, GitHub Copilot, Antigravity, Qwen, and other harnesses. See the [support status matrix](#platform-support) before assuming feature parity. -Access to 68 agents, 291 skills, and 94 legacy command shims, plus hooks, rules, memory, continuous learning, and AgentShield security scanning. The agents are specialized for planning, review, build repair, security, architecture, and domain work. +Access to 68 agents, 292 skills, and 94 legacy command shims, plus hooks, rules, memory, continuous learning, and AgentShield security scanning. The agents are specialized for planning, review, build repair, security, architecture, and domain work. | Included | Count | What it gives you | | ---------------- | ----------: | ------------------------------------------------------------------------------------ | | Agents | 68 agents | Planning, review, build repair, security, architecture, and domain work | -| Skills | 291 skills | TDD, research, security, docs, frontend, data, ML, operations, and more | +| Skills | 292 skills | TDD, research, security, docs, frontend, data, ML, operations, and more | | Commands | 94 commands | Convenient entry points while ECC moves to a skills-first surface | | Hooks and memory | Runtime | Enforcement, session summaries, continuous learning, instincts, and context controls | | Rules | Selective | Always-loaded standards you choose by language or project | diff --git a/README.zh-CN.md b/README.zh-CN.md index 214b978f7..e01fd54e2 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -196,7 +196,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/" /plugin list ecc@ecc ``` -**完成!** 你现在可以使用 68 个代理、291 个技能和 94 个命令。 +**完成!** 你现在可以使用 68 个代理、292 个技能和 94 个命令。 ### multi-* 命令需要额外配置 diff --git a/docs/tr/AGENTS.md b/docs/tr/AGENTS.md index 791e7f98a..c49b9962a 100644 --- a/docs/tr/AGENTS.md +++ b/docs/tr/AGENTS.md @@ -1,6 +1,6 @@ # Everything Claude Code (ECC) — Agent Talimatları -Bu, yazılım geliştirme için 68 özel agent, 291 skill, 94 command ve otomatik hook iş akışları sağlayan **üretime hazır bir AI kodlama eklentisidir**. +Bu, yazılım geliştirme için 68 özel agent, 292 skill, 94 command ve otomatik hook iş akışları sağlayan **üretime hazır bir AI kodlama eklentisidir**. **Sürüm:** 2.2.1 @@ -142,7 +142,7 @@ Başarısızlık sorunlarını giderin: test izolasyonunu kontrol edin → mockl ``` agents/ — 68 özel subagent -skills/ — 291 iş akışı skillleri ve alan bilgisi +skills/ — 292 iş akışı skillleri ve alan bilgisi commands/ — 94 slash command hooks/ — Tetikleyici tabanlı otomasyonlar rules/ — Her zaman uyulması gereken kurallar (ortak + dile özel) diff --git a/docs/zh-CN/AGENTS.md b/docs/zh-CN/AGENTS.md index e9141f30b..2f5a18856 100644 --- a/docs/zh-CN/AGENTS.md +++ b/docs/zh-CN/AGENTS.md @@ -1,6 +1,6 @@ # Everything Claude Code (ECC) — 智能体指令 -这是一个**生产就绪的 AI 编码插件**,提供 68 个专业代理、291 项技能、94 条命令以及自动化钩子工作流,用于软件开发。 +这是一个**生产就绪的 AI 编码插件**,提供 68 个专业代理、292 项技能、94 条命令以及自动化钩子工作流,用于软件开发。 **版本:** 2.2.1 @@ -147,7 +147,7 @@ ``` agents/ — 68 个专业子代理 -skills/ — 291 个工作流技能和领域知识 +skills/ — 292 个工作流技能和领域知识 commands/ — 94 个斜杠命令 hooks/ — 基于触发的自动化 rules/ — 始终遵循的指导方针(通用 + 每种语言) diff --git a/docs/zh-CN/README.md b/docs/zh-CN/README.md index 691c31a23..422f22d5d 100644 --- a/docs/zh-CN/README.md +++ b/docs/zh-CN/README.md @@ -260,7 +260,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/" /plugin list ecc@ecc ``` -**搞定!** 你现在可以使用 68 个智能体、291 项技能和 94 个命令了。 +**搞定!** 你现在可以使用 68 个智能体、292 项技能和 94 个命令了。 *** @@ -1174,7 +1174,7 @@ opencode |---------|---------------|----------|--------| | 智能体 | PASS: 68 个 | PASS: 12 个 | **Claude Code 领先** | | 命令 | PASS: 94 个 | PASS: 35 个 | **Claude Code 领先** | -| 技能 | PASS: 291 项 | PASS: 37 项 | **Claude Code 领先** | +| 技能 | PASS: 292 项 | PASS: 37 项 | **Claude Code 领先** | | 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** | | 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** | | MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** | @@ -1282,7 +1282,7 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以 |---------|-----------------------|------------|-----------|----------| | **智能体** | 68 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 | | **命令** | 94 | 共享 | 基于指令 | 35 | -| **技能** | 291 | 共享 | 10 (原生格式) | 37 | +| **技能** | 292 | 共享 | 10 (原生格式) | 37 | | **钩子事件** | 8 种类型 | 15 种类型 | SessionStart(1 种类型) | 11 种类型 | | **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | 1 个 SessionStart 引导脚本 | 插件钩子 | | **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 | diff --git a/manifests/install-modules.json b/manifests/install-modules.json index 92a73e08d..8c3839daa 100644 --- a/manifests/install-modules.json +++ b/manifests/install-modules.json @@ -423,6 +423,7 @@ "kind": "skills", "description": "Security review and security-focused framework guidance.", "paths": [ + "skills/agent-security-hardening", "skills/defi-amm-security", "skills/django-security", "skills/healthcare-phi-compliance", diff --git a/package.json b/package.json index b3508b59a..db102afdc 100644 --- a/package.json +++ b/package.json @@ -149,6 +149,7 @@ "skills/agent-architecture-audit/", "skills/agent-harness-construction/", "skills/agent-introspection-debugging/", + "skills/agent-security-hardening/", "skills/agent-sort/", "skills/agentic-engineering/", "skills/agentic-os/", diff --git a/skills/agent-security-hardening/SKILL.md b/skills/agent-security-hardening/SKILL.md new file mode 100644 index 000000000..734461bec --- /dev/null +++ b/skills/agent-security-hardening/SKILL.md @@ -0,0 +1,152 @@ +--- +name: agent-security-hardening +description: Security hardening guidance for AI agent frameworks that process untrusted content, invoke tools, write workspace files, manage runtime identifiers, or handle credentials. Use when building or reviewing an agent runtime, autonomous worker, tool gateway, memory service, or multi-tenant agent deployment. Do not use for general web application security or offensive testing. +metadata: + origin: ECC +--- + +# Agent Security Hardening + +Harden the boundaries where an agent turns untrusted data into filesystem, network, memory, or tool actions. Produce concrete controls and tests for the framework being reviewed; a checklist without enforcement evidence is incomplete. + +## Important + +- Treat model output, retrieved content, tool output, memory, and cross-agent messages as untrusted input. +- Default external integrations to read-only and grant each write capability separately. +- Validate at the action boundary even when an upstream prompt or schema already validated the value. +- Fail closed before a side effect when identity, destination, ownership, or containment cannot be proven. +- Use limits, schemes, domains, and retention periods verified from the deployment configuration. When a value is absent, mark it as a required decision and test that the implementation rejects an unbounded configuration; do not invent a plausible value. + +## When to Activate + +- Building an autonomous agent, tool gateway, memory service, or agent-to-agent protocol +- Reviewing code that maps model output into commands, paths, URLs, credentials, or API mutations +- Isolating tenants, projects, sessions, or workspaces in a long-running agent process +- Hardening temporary files, logs, tool responses, or external-content ingestion + +## Hardening Workflow + +### 1. Map Trust Boundaries + +List every boundary as `source -> parser -> validator -> side effect`. Include direct user input, model output, retrieved documents, memory, environment variables, tool results, and messages from other agents. + +For each side effect, record: + +- the identity and scope authorizing it; +- the exact validator that runs immediately before it; +- the maximum input and output size; +- the audit evidence produced; +- the recovery path if the operation is interrupted. + +This step is complete when every write, command, network mutation, and credential use has one named authorization boundary. + +### 2. Validate Identifiers, Environment Values, and URLs + +Use an allowlist for identifiers that become filenames, keys, selectors, or command arguments: + +```python +import re + +SAFE_AGENT_ID = re.compile(r"^[A-Za-z0-9_-]{1,64}$") + +def require_agent_id(value: object) -> str: + if not isinstance(value, str) or not SAFE_AGENT_ID.fullmatch(value): + raise ValueError("invalid agent identifier") + return value +``` + +Reject missing and whitespace-only environment values before constructing a client: + +```python +def require_env(name: str, environ: dict[str, str]) -> str: + value = environ.get(name) + if value is None or not value.strip(): + raise RuntimeError(f"{name} is required") + return value +``` + +Parse URLs, allow only schemes required by the deployment, require a host, reject embedded credentials, and resolve or block private-network destinations when the URL is attacker-controlled. Revalidate redirects because they can cross the original trust boundary. + +This step is complete when tests reject empty, oversized, malformed, traversal-shaped, credential-bearing, and unauthorized-destination values before any side effect. + +### 3. Contain Filesystem Access + +Reject absolute user-controlled paths and traversal components before joining. Resolve both the workspace and candidate path, then prove containment: + +```python +from pathlib import Path + +def workspace_path(workspace: Path, requested: str) -> Path: + relative = Path(requested) + if relative.is_absolute() or ".." in relative.parts: + raise ValueError("path must be workspace-relative") + + root = workspace.resolve(strict=True) + candidate = (root / relative).resolve(strict=False) + if not candidate.is_relative_to(root): + raise ValueError("path escapes workspace") + return candidate +``` + +Containment checks do not eliminate symlink races. For sensitive writes, open relative to a trusted directory handle where the platform supports it, reject symlink targets, create files exclusively, and verify ownership and permissions after opening. + +This step is complete when tests cover sibling-prefix paths, nested `..`, absolute paths, symlink escapes, and a valid nested workspace path. + +### 4. Protect Temporary Data and Credentials + +- Create sensitive files with owner-only permissions such as `0o600`; create private directories with `0o700`. +- Prefer a user-private runtime directory over a shared temporary directory for credentials or tenant data. +- Create unpredictable names atomically; do not check-then-create. +- Remove temporary data in `finally`, while preserving a redacted error record when cleanup fails. +- Keep secrets out of command arguments, model context, logs, traces, exception text, and tool responses. +- Pass secrets through the platform's secret store or scoped environment injection and rotate exposed credentials. +- Treat zeroization as best effort: mutable byte buffers can be overwritten, but immutable language strings cannot be reliably erased. + +This step is complete when file-mode checks, forced-error cleanup tests, and log-capture tests show that secret values never persist outside the approved boundary. + +### 5. Bound and Sanitize Tool Output + +Read tool output with a byte limit rather than collecting an unbounded stream. Preserve structured fields, remove disallowed control characters, and append an explicit truncation marker with the original byte count. Keep a content hash when later forensic comparison matters. + +Do not silently turn malformed output into an empty success value. Return a typed failure that names the producing tool, boundary, and validation reason without echoing sensitive content. + +This step is complete when oversized output, invalid encoding, terminal-control sequences, malformed structured data, and a normal response all have deterministic tests. + +## Verification Gate + +Before calling the framework hardened, verify all of these behaviors through its real entry point: + +1. Untrusted instructions remain data and cannot trigger a tool or write. +2. Cross-tenant and cross-workspace identifiers are rejected at the action boundary. +3. Traversal and symlink escape attempts leave the filesystem unchanged. +4. Forced failures leave no credential-bearing temp files or logs. +5. Oversized or malformed tool output returns an explicit bounded error. + +Report the command used, exit status, rejected input, and observed absence of the side effect. Mock-only tests do not prove operating-system permissions, symlink handling, subprocess isolation, or network egress controls. + +## Common Issues + +### Validation exists only in the prompt + +Prompt instructions are advisory. Move the same invariant into deterministic code immediately before the side effect and test a model response that violates it. + +### A normalized path still escapes + +String prefix checks confuse sibling paths such as `/work/app` and `/work/application`. Resolve paths and use component-aware containment, then test symlinks separately. + +### Redaction happens after logging + +Redact before values enter the logger, tracer, exception, or model context. Add a capture test using a sentinel secret and assert the sentinel is absent from every emitted channel. + +## When NOT to Use + +- Use `security-review` for ordinary application authentication, authorization, SQL injection, XSS, CSRF, and API security. +- Use `security-scan` for repository-wide automated vulnerability scanning. +- Use infrastructure-specific guidance for host, container, firewall, or cloud hardening. +- Do not use this skill for penetration testing, exploit development, or unauthorized access. + +## Related Skills + +- `agent-harness-construction` +- `security-review` +- `security-scan` From 3f3566fa111ab4bfcf4c4418926360890d953ba5 Mon Sep 17 00:00:00 2001 From: vam Date: Tue, 8 Sep 2026 11:51:19 +0800 Subject: [PATCH 2/8] fix(skills): use configured agent ID limits --- skills/agent-security-hardening/SKILL.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/skills/agent-security-hardening/SKILL.md b/skills/agent-security-hardening/SKILL.md index 734461bec..da09e69a7 100644 --- a/skills/agent-security-hardening/SKILL.md +++ b/skills/agent-security-hardening/SKILL.md @@ -42,15 +42,21 @@ This step is complete when every write, command, network mutation, and credentia ### 2. Validate Identifiers, Environment Values, and URLs -Use an allowlist for identifiers that become filenames, keys, selectors, or command arguments: +Use an allowlist for identifiers that become filenames, keys, selectors, or command arguments. Pass the maximum length from verified deployment configuration instead of embedding a sample policy: ```python import re -SAFE_AGENT_ID = re.compile(r"^[A-Za-z0-9_-]{1,64}$") +SAFE_AGENT_ID_CHARS = re.compile(r"^[A-Za-z0-9_-]+$") -def require_agent_id(value: object) -> str: - if not isinstance(value, str) or not SAFE_AGENT_ID.fullmatch(value): +def require_agent_id(value: object, *, max_length: int) -> str: + if isinstance(max_length, bool) or not isinstance(max_length, int) or max_length < 1: + raise RuntimeError("configured agent ID limit must be a positive integer") + if ( + not isinstance(value, str) + or len(value) > max_length + or not SAFE_AGENT_ID_CHARS.fullmatch(value) + ): raise ValueError("invalid agent identifier") return value ``` From 3eb0eefbb31659e9105965533f43d240f2761bc3 Mon Sep 17 00:00:00 2001 From: vam Date: Tue, 8 Sep 2026 12:02:06 +0800 Subject: [PATCH 3/8] fix(skills): harden action and redirect boundaries --- skills/agent-security-hardening/SKILL.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/skills/agent-security-hardening/SKILL.md b/skills/agent-security-hardening/SKILL.md index da09e69a7..509eeed62 100644 --- a/skills/agent-security-hardening/SKILL.md +++ b/skills/agent-security-hardening/SKILL.md @@ -15,6 +15,7 @@ Harden the boundaries where an agent turns untrusted data into filesystem, netwo - Default external integrations to read-only and grant each write capability separately. - Validate at the action boundary even when an upstream prompt or schema already validated the value. - Fail closed before a side effect when identity, destination, ownership, or containment cannot be proven. +- Before a delete, publish, credential rotation, or other irreversible action, the real tool entry point must authorize the authenticated principal for that exact operation and resource scope, then require explicit confirmation or a valid scoped pre-approval. Missing or invalid evidence blocks the action. - Use limits, schemes, domains, and retention periods verified from the deployment configuration. When a value is absent, mark it as a required decision and test that the implementation rejects an unbounded configuration; do not invent a plausible value. ## When to Activate @@ -71,9 +72,9 @@ def require_env(name: str, environ: dict[str, str]) -> str: return value ``` -Parse URLs, allow only schemes required by the deployment, require a host, reject embedded credentials, and resolve or block private-network destinations when the URL is attacker-controlled. Revalidate redirects because they can cross the original trust boundary. +Parse URLs, allow only schemes required by the deployment, require a host, reject embedded credentials, and block private-network destinations when the URL is attacker-controlled. For every outbound connection and redirect hop, pin the connection to a validated public address or verify the connected peer address at connection time so DNS rebinding cannot cross the boundary. A credential-bearing request must use HTTPS with certificate verification before credentials are attached. Disable automatic cross-origin credential forwarding; when the scheme or origin changes, strip the credentials and reauthorize the new destination or reject the redirect. Re-run destination, connected-address, and transport validation on every hop. -This step is complete when tests reject empty, oversized, malformed, traversal-shaped, credential-bearing, and unauthorized-destination values before any side effect. +This step is complete when tests reject empty, oversized, malformed, traversal-shaped, credential-bearing, and unauthorized-destination values before any side effect, and prove that DNS rebinding and cross-origin redirects cannot carry credentials to an unapproved peer. ### 3. Contain Filesystem Access @@ -123,10 +124,11 @@ This step is complete when oversized output, invalid encoding, terminal-control Before calling the framework hardened, verify all of these behaviors through its real entry point: 1. Untrusted instructions remain data and cannot trigger a tool or write. -2. Cross-tenant and cross-workspace identifiers are rejected at the action boundary. -3. Traversal and symlink escape attempts leave the filesystem unchanged. -4. Forced failures leave no credential-bearing temp files or logs. -5. Oversized or malformed tool output returns an explicit bounded error. +2. An irreversible action without operation-and-resource authorization plus confirmation or scoped pre-approval leaves no side effect. +3. Cross-tenant and cross-workspace identifiers are rejected at the action boundary. +4. Traversal and symlink escape attempts leave the filesystem unchanged. +5. Forced failures leave no credential-bearing temp files or logs. +6. Oversized or malformed tool output returns an explicit bounded error. Report the command used, exit status, rejected input, and observed absence of the side effect. Mock-only tests do not prove operating-system permissions, symlink handling, subprocess isolation, or network egress controls. From 30e403b9bc2bc2c7cd46b65bcef9c3a2b392eecc Mon Sep 17 00:00:00 2001 From: vam Date: Tue, 8 Sep 2026 12:12:19 +0800 Subject: [PATCH 4/8] fix(skills): prevent option-shaped agent IDs --- skills/agent-security-hardening/SKILL.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/skills/agent-security-hardening/SKILL.md b/skills/agent-security-hardening/SKILL.md index 509eeed62..c616ebf4c 100644 --- a/skills/agent-security-hardening/SKILL.md +++ b/skills/agent-security-hardening/SKILL.md @@ -48,7 +48,7 @@ Use an allowlist for identifiers that become filenames, keys, selectors, or comm ```python import re -SAFE_AGENT_ID_CHARS = re.compile(r"^[A-Za-z0-9_-]+$") +SAFE_AGENT_ID_CHARS = re.compile(r"^[A-Za-z0-9_][A-Za-z0-9_-]*$") def require_agent_id(value: object, *, max_length: int) -> str: if isinstance(max_length, bool) or not isinstance(max_length, int) or max_length < 1: @@ -62,6 +62,8 @@ def require_agent_id(value: object, *, max_length: int) -> str: return value ``` +When an identifier becomes a CLI argument, pass it through an argv API and place it after the command's end-of-options marker where supported. Character validation is not a substitute for argument separation. + Reject missing and whitespace-only environment values before constructing a client: ```python From 6b1fd318ce1c709413505c7704f5d552a6733305 Mon Sep 17 00:00:00 2001 From: vam Date: Tue, 8 Sep 2026 13:47:42 +0800 Subject: [PATCH 5/8] fix(skills): enforce issue-defined agent ID cap --- skills/agent-security-hardening/SKILL.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/skills/agent-security-hardening/SKILL.md b/skills/agent-security-hardening/SKILL.md index c616ebf4c..54a272080 100644 --- a/skills/agent-security-hardening/SKILL.md +++ b/skills/agent-security-hardening/SKILL.md @@ -16,7 +16,7 @@ Harden the boundaries where an agent turns untrusted data into filesystem, netwo - Validate at the action boundary even when an upstream prompt or schema already validated the value. - Fail closed before a side effect when identity, destination, ownership, or containment cannot be proven. - Before a delete, publish, credential rotation, or other irreversible action, the real tool entry point must authorize the authenticated principal for that exact operation and resource scope, then require explicit confirmation or a valid scoped pre-approval. Missing or invalid evidence blocks the action. -- Use limits, schemes, domains, and retention periods verified from the deployment configuration. When a value is absent, mark it as a required decision and test that the implementation rejects an unbounded configuration; do not invent a plausible value. +- Use hard bounds defined by the governing requirement and stricter values verified from deployment configuration. When neither defines a value, mark it as a required decision and test that the implementation rejects an unbounded configuration; do not invent a plausible value. ## When to Activate @@ -43,16 +43,21 @@ This step is complete when every write, command, network mutation, and credentia ### 2. Validate Identifiers, Environment Values, and URLs -Use an allowlist for identifiers that become filenames, keys, selectors, or command arguments. Pass the maximum length from verified deployment configuration instead of embedding a sample policy: +Use an allowlist for identifiers that become filenames, keys, selectors, or command arguments. Agent IDs have the issue-defined hard maximum of 64 characters; deployment configuration may choose a stricter positive limit but cannot raise that boundary: ```python import re SAFE_AGENT_ID_CHARS = re.compile(r"^[A-Za-z0-9_][A-Za-z0-9_-]*$") +HARD_MAX_AGENT_ID_LENGTH = 64 def require_agent_id(value: object, *, max_length: int) -> str: - if isinstance(max_length, bool) or not isinstance(max_length, int) or max_length < 1: - raise RuntimeError("configured agent ID limit must be a positive integer") + if ( + isinstance(max_length, bool) + or not isinstance(max_length, int) + or not 1 <= max_length <= HARD_MAX_AGENT_ID_LENGTH + ): + raise RuntimeError("configured agent ID limit must be between 1 and 64") if ( not isinstance(value, str) or len(value) > max_length From c196dc7d51128e88a5e208ab7739b1a75ecc6879 Mon Sep 17 00:00:00 2001 From: vam Date: Sun, 27 Sep 2026 20:34:49 +0800 Subject: [PATCH 6/8] fix(skills): reject Windows-unsafe components in workspace_path example Reject reserved device names (including with extensions and the superscript COM/LPT variants), trailing dots or spaces, and colon stream syntax on every platform, and execute the documented snippet in a regression test. --- skills/agent-security-hardening/SKILL.md | 13 +- .../ci/agent-security-hardening-skill.test.js | 136 ++++++++++++++++++ 2 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 tests/ci/agent-security-hardening-skill.test.js diff --git a/skills/agent-security-hardening/SKILL.md b/skills/agent-security-hardening/SKILL.md index 54a272080..d392c8823 100644 --- a/skills/agent-security-hardening/SKILL.md +++ b/skills/agent-security-hardening/SKILL.md @@ -85,15 +85,24 @@ This step is complete when tests reject empty, oversized, malformed, traversal-s ### 3. Contain Filesystem Access -Reject absolute user-controlled paths and traversal components before joining. Resolve both the workspace and candidate path, then prove containment: +Reject absolute user-controlled paths and traversal components before joining. Reject Windows-unsafe components on every platform so behavior stays portable: reserved device names (`CON`, `NUL`, `COM1`, `LPT1`, including with an extension such as `nul.txt`), names ending in a dot or space (Win32 strips them), and `:` (drive-relative or NTFS alternate data stream syntax such as `report.txt:ads`). Resolve both the workspace and candidate path, then prove containment: ```python +import re from pathlib import Path +WINDOWS_RESERVED_NAME = re.compile( + r"(CON|PRN|AUX|NUL|COM[1-9¹²³]|LPT[1-9¹²³])(\..*)?", + re.IGNORECASE, +) + def workspace_path(workspace: Path, requested: str) -> Path: relative = Path(requested) if relative.is_absolute() or ".." in relative.parts: raise ValueError("path must be workspace-relative") + for part in relative.parts: + if ":" in part or part.endswith((".", " ")) or WINDOWS_RESERVED_NAME.fullmatch(part): + raise ValueError("path component is not portable") root = workspace.resolve(strict=True) candidate = (root / relative).resolve(strict=False) @@ -104,7 +113,7 @@ def workspace_path(workspace: Path, requested: str) -> Path: Containment checks do not eliminate symlink races. For sensitive writes, open relative to a trusted directory handle where the platform supports it, reject symlink targets, create files exclusively, and verify ownership and permissions after opening. -This step is complete when tests cover sibling-prefix paths, nested `..`, absolute paths, symlink escapes, and a valid nested workspace path. +This step is complete when tests cover sibling-prefix paths, nested `..`, absolute paths, symlink escapes, reserved device names, trailing dots or spaces, alternate data stream syntax, and a valid nested workspace path. ### 4. Protect Temporary Data and Credentials diff --git a/tests/ci/agent-security-hardening-skill.test.js b/tests/ci/agent-security-hardening-skill.test.js new file mode 100644 index 000000000..5cc662046 --- /dev/null +++ b/tests/ci/agent-security-hardening-skill.test.js @@ -0,0 +1,136 @@ +/** + * Executes the workspace_path() example from the agent-security-hardening + * skill so the documented guard is checked as code, not only as prose. + */ + +"use strict"; + +const assert = require("assert"); +const fs = require("fs"); +const os = require("os"); +const path = require("path"); +const { spawnSync } = require("child_process"); + +const REPO_ROOT = path.join(__dirname, "..", ".."); +const SKILL_PATH = path.join(REPO_ROOT, "skills", "agent-security-hardening", "SKILL.md"); + +function findPython() { + for (const candidate of ["python3", "python"]) { + const probe = spawnSync(candidate, ["-c", "import sys; print(sys.version_info >= (3, 9))"], { + encoding: "utf8", + }); + if (probe.status === 0 && probe.stdout.trim() === "True") return candidate; + } + return null; +} + +function workspacePathSnippet() { + const skill = fs.readFileSync(SKILL_PATH, "utf8"); + const blocks = [...skill.matchAll(/```python\n([\s\S]*?)```/g)].map(match => match[1]); + const snippet = blocks.find(block => block.includes("def workspace_path(")); + assert.ok(snippet, "workspace_path() python example missing from SKILL.md"); + return snippet; +} + +const HARNESS = ` +import json, sys +from pathlib import Path +namespace = {} +exec(sys.stdin.read(), namespace) +workspace_path = namespace["workspace_path"] +workspace = Path(sys.argv[1]) +results = {} +for requested in json.loads(sys.argv[2]): + try: + workspace_path(workspace, requested) + results[requested] = "accepted" + except ValueError: + results[requested] = "rejected" +print(json.dumps(results)) +`; + +function classify(python, requests) { + const workspace = fs.mkdtempSync(path.join(os.tmpdir(), "ecc-workspace-path-")); + try { + const result = spawnSync(python, ["-c", HARNESS, workspace, JSON.stringify(requests)], { + input: workspacePathSnippet(), + encoding: "utf8", + }); + assert.strictEqual(result.status, 0, `python harness failed:\n${result.stderr}`); + return JSON.parse(result.stdout); + } finally { + fs.rmSync(workspace, { recursive: true, force: true }); + } +} + +const tests = []; +function test(name, fn) { tests.push([name, fn]); } + +const python = findPython(); + +test("accepts ordinary nested and look-alike workspace paths", () => { + if (!python) return { skipped: "python >= 3.9 not available" }; + const requests = ["reports/q1.txt", "console.txt", "CONFIG", "com10.txt", "nullable.md", ".env"]; + const results = classify(python, requests); + for (const requested of requests) { + assert.strictEqual(results[requested], "accepted", `${requested} should be accepted`); + } +}); + +test("rejects traversal and absolute paths", () => { + if (!python) return { skipped: "python >= 3.9 not available" }; + const requests = ["../outside.txt", "nested/../../outside.txt", path.resolve(os.tmpdir(), "abs.txt")]; + const results = classify(python, requests); + for (const requested of requests) { + assert.strictEqual(results[requested], "rejected", `${requested} should be rejected`); + } +}); + +test("rejects Windows reserved device names, trailing dots or spaces, and stream syntax", () => { + if (!python) return { skipped: "python >= 3.9 not available" }; + const requests = [ + "CON", + "nul", + "nul.txt", + "NUL.tar.gz", + "aux", + "prn.log", + "com1", + "COM9.txt", + "lpt1", + "LPT9.log", + "COM¹", + "lpt³.txt", + "sub/CON/file.txt", + "notes.", + "notes ", + "dir./file.txt", + "report.txt:ads", + "report.txt::$DATA", + ]; + const results = classify(python, requests); + for (const requested of requests) { + assert.strictEqual(results[requested], "rejected", `${JSON.stringify(requested)} should be rejected`); + } +}); + +let failed = 0; +let skipped = 0; +console.log("\n=== Testing agent-security-hardening skill ===\n"); +for (const [name, fn] of tests) { + try { + const result = fn(); + if (result?.skipped) { + skipped += 1; + console.log(` - SKIP ${name}: ${result.skipped}`); + continue; + } + console.log(` ✓ ${name}`); + } catch (error) { + failed += 1; + console.log(` ✗ ${name}`); + console.error(` ${error.message}`); + } +} +if (failed) process.exit(1); +console.log(`\n${tests.length - failed - skipped}/${tests.length} passed, ${skipped} skipped`); From 995d3c493416f52afab87e5e9a1e35f088249a70 Mon Sep 17 00:00:00 2001 From: vam Date: Sun, 27 Sep 2026 21:08:55 +0800 Subject: [PATCH 7/8] docs: update skill counts for agent-security-hardening Run catalog:sync and update the Japanese and Turkish counts that the sync script does not cover, so the catalog reports 293 skills. --- .claude-plugin/marketplace.json | 2 +- .claude-plugin/plugin.json | 2 +- AGENTS.md | 4 ++-- README.md | 4 ++-- README.zh-CN.md | 2 +- docs/ja-JP/README.md | 2 +- docs/tr/AGENTS.md | 4 ++-- docs/zh-CN/AGENTS.md | 4 ++-- docs/zh-CN/README.md | 6 +++--- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index b19c87b8d..d5385a381 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -11,7 +11,7 @@ { "name": "ecc", "source": "./", - "description": "Harness-native ECC operator layer - 68 agents, 292 skills, 94 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses", + "description": "Harness-native ECC operator layer - 68 agents, 293 skills, 94 legacy command shims, reusable hooks, rules, selective install profiles, and production-ready workflows for Claude Code, Codex, OpenCode, Cursor, and related agent harnesses", "version": "2.2.2", "author": { "name": "Affaan Mustafa", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 072edddfe..8871e37d2 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "ecc", "version": "2.2.2", - "description": "Harness-native ECC plugin for engineering teams - 68 agents, 292 skills, 94 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses", + "description": "Harness-native ECC plugin for engineering teams - 68 agents, 293 skills, 94 legacy command shims, reusable hooks, rules, MCP conventions, and operator workflows for Claude Code plus adjacent agent harnesses", "author": { "name": "Affaan Mustafa", "url": "https://x.com/affaanmustafa" diff --git a/AGENTS.md b/AGENTS.md index 17330b848..c6c5acd98 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # Everything Claude Code (ECC) — Agent Instructions -This is a **production-ready AI coding plugin** providing 68 specialized agents, 292 skills, 94 commands, and automated hook workflows for software development. +This is a **production-ready AI coding plugin** providing 68 specialized agents, 293 skills, 94 commands, and automated hook workflows for software development. **Version:** 2.2.2 @@ -154,7 +154,7 @@ Troubleshoot failures: check test isolation → verify mocks → fix implementat ``` agents/ — 68 specialized subagents -skills/ — 292 workflow skills and domain knowledge +skills/ — 293 workflow skills and domain knowledge commands/ — 94 slash commands hooks/ — Trigger-based automations rules/ — Always-follow guidelines (common + per-language) diff --git a/README.md b/README.md index 117552c2b..ef7ad4249 100644 --- a/README.md +++ b/README.md @@ -138,12 +138,12 @@ Instead of rebuilding that process in every prompt, you install it once and make ECC is MIT-licensed open source. It works best with Claude Code today, has a supported Codex sync path, and provides capability-limited adapters for Cursor, OpenCode, Gemini, Zed, GitHub Copilot, Antigravity, Qwen, and other harnesses. See the [support status matrix](#platform-support) before assuming feature parity. -Access to 68 agents, 292 skills, and 94 legacy command shims, plus hooks, rules, memory, continuous learning, and AgentShield security scanning. The agents are specialized for planning, review, build repair, security, architecture, and domain work. +Access to 68 agents, 293 skills, and 94 legacy command shims, plus hooks, rules, memory, continuous learning, and AgentShield security scanning. The agents are specialized for planning, review, build repair, security, architecture, and domain work. | Included | Count | What it gives you | | ---------------- | ----------: | ------------------------------------------------------------------------------------ | | Agents | 68 agents | Planning, review, build repair, security, architecture, and domain work | -| Skills | 292 skills | TDD, research, security, docs, frontend, data, ML, operations, and more | +| Skills | 293 skills | TDD, research, security, docs, frontend, data, ML, operations, and more | | Commands | 94 commands | Convenient entry points while ECC moves to a skills-first surface | | Hooks and memory | Runtime | Enforcement, session summaries, continuous learning, instincts, and context controls | | Rules | Selective | Always-loaded standards you choose by language or project | diff --git a/README.zh-CN.md b/README.zh-CN.md index 552d69b58..e8aacbbd9 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -196,7 +196,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/" /plugin list ecc@ecc ``` -**完成!** 你现在可以使用 68 个代理、292 个技能和 94 个命令。 +**完成!** 你现在可以使用 68 个代理、293 个技能和 94 个命令。 ### multi-* 命令需要额外配置 diff --git a/docs/ja-JP/README.md b/docs/ja-JP/README.md index 00cc8b62f..209e8e411 100644 --- a/docs/ja-JP/README.md +++ b/docs/ja-JP/README.md @@ -141,7 +141,7 @@ ECC は MIT ライセンスのオープンソースです。現時点では Clau | 含まれるもの | 数 | 得られるもの | | ---------------- | ----------: | ------------------------------------------------------------------------------------ | | Agents | 68 agents | 計画、レビュー、ビルド修復、セキュリティ、アーキテクチャ、ドメイン作業 | -| Skills | 292 skills | TDD、リサーチ、セキュリティ、ドキュメント、フロントエンド、データ、ML、運用など | +| Skills | 293 skills | TDD、リサーチ、セキュリティ、ドキュメント、フロントエンド、データ、ML、運用など | | Commands | 95 commands | ECC が skills ファーストの構成へ移行する間の便利なエントリーポイント | | Hooks とメモリ | ランタイム | 強制、セッションサマリー、継続的学習、instincts、コンテキスト制御 | | Rules | 選択式 | 言語やプロジェクトごとに選ぶ、常時ロードされる標準 | diff --git a/docs/tr/AGENTS.md b/docs/tr/AGENTS.md index a67004d7b..53e74b8f1 100644 --- a/docs/tr/AGENTS.md +++ b/docs/tr/AGENTS.md @@ -1,6 +1,6 @@ # Everything Claude Code (ECC) — Agent Talimatları -Bu, yazılım geliştirme için 68 özel agent, 292 skill, 94 command ve otomatik hook iş akışları sağlayan **üretime hazır bir AI kodlama eklentisidir**. +Bu, yazılım geliştirme için 68 özel agent, 293 skill, 94 command ve otomatik hook iş akışları sağlayan **üretime hazır bir AI kodlama eklentisidir**. **Sürüm:** 2.2.2 @@ -142,7 +142,7 @@ Başarısızlık sorunlarını giderin: test izolasyonunu kontrol edin → mockl ``` agents/ — 68 özel subagent -skills/ — 292 iş akışı skillleri ve alan bilgisi +skills/ — 293 iş akışı skillleri ve alan bilgisi commands/ — 94 slash command hooks/ — Tetikleyici tabanlı otomasyonlar rules/ — Her zaman uyulması gereken kurallar (ortak + dile özel) diff --git a/docs/zh-CN/AGENTS.md b/docs/zh-CN/AGENTS.md index 31e1a3817..555599e2e 100644 --- a/docs/zh-CN/AGENTS.md +++ b/docs/zh-CN/AGENTS.md @@ -1,6 +1,6 @@ # Everything Claude Code (ECC) — 智能体指令 -这是一个**生产就绪的 AI 编码插件**,提供 68 个专业代理、292 项技能、94 条命令以及自动化钩子工作流,用于软件开发。 +这是一个**生产就绪的 AI 编码插件**,提供 68 个专业代理、293 项技能、94 条命令以及自动化钩子工作流,用于软件开发。 **版本:** 2.2.2 @@ -147,7 +147,7 @@ ``` agents/ — 68 个专业子代理 -skills/ — 292 个工作流技能和领域知识 +skills/ — 293 个工作流技能和领域知识 commands/ — 94 个斜杠命令 hooks/ — 基于触发的自动化 rules/ — 始终遵循的指导方针(通用 + 每种语言) diff --git a/docs/zh-CN/README.md b/docs/zh-CN/README.md index 3228c6159..e5ed37377 100644 --- a/docs/zh-CN/README.md +++ b/docs/zh-CN/README.md @@ -260,7 +260,7 @@ Copy-Item -Recurse rules/typescript "$HOME/.claude/rules/" /plugin list ecc@ecc ``` -**搞定!** 你现在可以使用 68 个智能体、292 项技能和 94 个命令了。 +**搞定!** 你现在可以使用 68 个智能体、293 项技能和 94 个命令了。 *** @@ -1174,7 +1174,7 @@ opencode |---------|---------------|----------|--------| | 智能体 | PASS: 68 个 | PASS: 12 个 | **Claude Code 领先** | | 命令 | PASS: 94 个 | PASS: 35 个 | **Claude Code 领先** | -| 技能 | PASS: 292 项 | PASS: 37 项 | **Claude Code 领先** | +| 技能 | PASS: 293 项 | PASS: 37 项 | **Claude Code 领先** | | 钩子 | PASS: 8 种事件类型 | PASS: 11 种事件 | **OpenCode 更多!** | | 规则 | PASS: 29 条 | PASS: 13 条指令 | **Claude Code 领先** | | MCP 服务器 | PASS: 14 个 | PASS: 完整 | **完全对等** | @@ -1282,7 +1282,7 @@ ECC 是**第一个最大化利用每个主要 AI 编码工具的插件**。以 |---------|-----------------------|------------|-----------|----------| | **智能体** | 68 | 共享 (AGENTS.md) | 共享 (AGENTS.md) | 12 | | **命令** | 94 | 共享 | 基于指令 | 35 | -| **技能** | 292 | 共享 | 10 (原生格式) | 37 | +| **技能** | 293 | 共享 | 10 (原生格式) | 37 | | **钩子事件** | 8 种类型 | 15 种类型 | SessionStart(1 种类型) | 11 种类型 | | **钩子脚本** | 20+ 个脚本 | 16 个脚本 (DRY 适配器) | 1 个 SessionStart 引导脚本 | 插件钩子 | | **规则** | 34 (通用 + 语言) | 34 (YAML 前页) | 基于指令 | 13 条指令 | From 9e951e266dae187d5270c8a9ec79909ced8774e8 Mon Sep 17 00:00:00 2001 From: haelyra <49814733+haelyra@users.noreply.github.com> Date: Fri, 9 Oct 2026 15:49:31 -0400 Subject: [PATCH 8/8] fix: normalize portable workspace path separators --- skills/agent-security-hardening/SKILL.md | 3 ++- tests/ci/agent-security-hardening-skill.test.js | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/skills/agent-security-hardening/SKILL.md b/skills/agent-security-hardening/SKILL.md index d392c8823..726cd3e2e 100644 --- a/skills/agent-security-hardening/SKILL.md +++ b/skills/agent-security-hardening/SKILL.md @@ -97,7 +97,8 @@ WINDOWS_RESERVED_NAME = re.compile( ) def workspace_path(workspace: Path, requested: str) -> Path: - relative = Path(requested) + # Treat both platform separators consistently before checking components. + relative = Path(requested.replace("\\", "/")) if relative.is_absolute() or ".." in relative.parts: raise ValueError("path must be workspace-relative") for part in relative.parts: diff --git a/tests/ci/agent-security-hardening-skill.test.js b/tests/ci/agent-security-hardening-skill.test.js index 5cc662046..b2c1b7fce 100644 --- a/tests/ci/agent-security-hardening-skill.test.js +++ b/tests/ci/agent-security-hardening-skill.test.js @@ -70,7 +70,7 @@ const python = findPython(); test("accepts ordinary nested and look-alike workspace paths", () => { if (!python) return { skipped: "python >= 3.9 not available" }; - const requests = ["reports/q1.txt", "console.txt", "CONFIG", "com10.txt", "nullable.md", ".env"]; + const requests = ["reports/q1.txt", "reports\\q1.txt", "console.txt", "CONFIG", "com10.txt", "nullable.md", ".env"]; const results = classify(python, requests); for (const requested of requests) { assert.strictEqual(results[requested], "accepted", `${requested} should be accepted`); @@ -79,7 +79,7 @@ test("accepts ordinary nested and look-alike workspace paths", () => { test("rejects traversal and absolute paths", () => { if (!python) return { skipped: "python >= 3.9 not available" }; - const requests = ["../outside.txt", "nested/../../outside.txt", path.resolve(os.tmpdir(), "abs.txt")]; + const requests = ["../outside.txt", "nested/../../outside.txt", "..\\outside.txt", "nested\\..\\..\\outside.txt", "\\\\server\\share\\file.txt", path.resolve(os.tmpdir(), "abs.txt")]; const results = classify(python, requests); for (const requested of requests) { assert.strictEqual(results[requested], "rejected", `${requested} should be rejected`); @@ -102,11 +102,14 @@ test("rejects Windows reserved device names, trailing dots or spaces, and stream "COM¹", "lpt³.txt", "sub/CON/file.txt", + "sub\\CON\\file.txt", "notes.", "notes ", "dir./file.txt", "report.txt:ads", "report.txt::$DATA", + "C:\\Windows\\system32", + "C:/Windows/system32", ]; const results = classify(python, requests); for (const requested of requests) {