Back to skill

Security audit

consensus-code-merge-guard

Security checks for vulnerabilities and agentic risk

Overview

The skill is broadly aligned with merge-governance use, but its implementation can approve and persist merge decisions without the existing persona governance the documentation says is required.

Review this skill carefully before using it to gate real merges. Its filesystem state writes and npm install path are disclosed and proportionate, but it should not be treated as an authoritative merge control until persona-set absence fails closed and external votes and constraints are strictly validated.

Vulnerability Patterns
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Agent Memory PoisoningWrites attacker-controlled rules into memory that affect later sessions
  • Remote Payload Retrieval and ExecutionFetches external code whose behavior can change after review
  • Embedded Malicious CodeShips malicious scripts inside the skill and executes them locally
Findings (2)

T09 · Insecure Skill Coding Practices

Error
Location
src/index.mjs:23
Finding
Missing persona sets silently fall back to synthetic approving personas<![CDATA[ ## Vulnerability Details **File Location**: `src/index.mjs`, line 23 **Vulnerability Type**: Fail-open governance bypass **Risk Level**: High ### Vulnerable Code ```js if(!ps && !externalMode){ ps={ persona_set_id:null, personas:[1,2,3,4,5].map((n)=>({persona_id:`default-${n}`,name:`Default Persona ${n}`,reputation:0.5})) }; } ``` ### Technical Analysis In persona mode, the implementation attempts to load either the requested persona set or the latest persona-set artifact. If no such artifact exists, it does not reject the request. Instead, it silently creates five synthetic personas with equal reputations. These generated personas are subsequently passed to `makeVotes()`. Unless optional, caller-controlled constraints detect failing tests or one of a small set of security-related phrases, each synthetic persona votes `YES`. This is a fail-open condition in a security and release-governance control. This behavior conflicts with the documented invocation contract in `SKILL.md`, which states that persona mode requires an existing `persona_set_id` and that the guard does not generate persona sets internally. The fallback can therefore produce an apparently governed `MERGE` decision despite the absence of the required governance state. ### Attack Path 1. An attacker or improperly configured caller invokes the guard in persona mode. 2. The caller omits `persona_set_id` when no latest persona set exists, or supplies an identifier that does not resolve to an existing persona set. 3. The caller omits `require_tests_pass` and `block_on_security_flags`, or supplies a change summary that does not match the narrow security-expression check. 4. The guard creates five synthetic personas rather than returning an error. 5. Each synthetic persona votes `YES`. 6. The votes are aggregated and can result in `MERGE`. 7. The decision is written to board state as an auditable decision artifact, making the bypass appear to be a legitimate governed outcome. ### Impact ...[truncated 529 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Remove the synthetic persona fallback entirely. - Return a fail-closed error when the requested persona set does not exist or when no latest persona set is available. - Require an explicit, valid `persona_set_id` in persona mode if that is the intended contract. - Validate that the loaded persona set contains a non-empty collection of uniquely identified personas with valid reputation values. - Load mandatory security and test policy from trusted board state rather than relying solely on caller-supplied constraints. - Add tests confirming that: - A missing `persona_set_id` is rejected when required. - A nonexistent persona set is rejected. - An empty or malformed persona set is rejected. - No decision artifact is written after persona-resolution failure. - Governance failures cannot produce a `MERGE` result. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/index.mjs:6
Finding
External votes and security constraints are accepted without strict schema validation<![CDATA[ ## Vulnerability Details **File Location**: `src/index.mjs`, lines 6 and 25 **Vulnerability Type**: Unvalidated governance input **Risk Level**: Medium ### Vulnerable Code ```js function validate(i){ if(!i||typeof i!=='object') return 'input must be object'; let e=rejectUnknown(i,TOP,'input'); if(e) return e; if(typeof i.board_id!=='string') return 'board_id required'; if(!i.change_summary||typeof i.change_summary!=='object') return 'change_summary required'; e=rejectUnknown(i.change_summary,CHG,'change_summary'); if(e) return e; if(i.mode!==undefined && !['persona','external_agent'].includes(i.mode)) return 'mode must be persona|external_agent'; if(i.external_votes!==undefined && !Array.isArray(i.external_votes)) return 'external_votes must be array'; return null; } ``` ```js const votes=externalMode ? input.external_votes : makeVotes(ps,input.change_summary,input.constraints||{}); ``` ### Technical Analysis The validator only verifies that `external_votes`, when supplied, is an array. It does not validate individual vote records, permitted vote values, voter identity, duplicate voters, confidence values, reputation or weight fields, red flags, or required properties. In external-agent mode, the array is passed directly to `aggregateVotes()`. The `constraints` property is allowed at the top level but is not verified to be an object and has no strict schema. Unknown constraint fields are not rejected, mandatory controls are not enforced, and `require_tests_pass` and `block_on_security_flags` are not required to be booleans. Because these controls are optional and caller-controlled, a caller can omit them to disable the checks in `makeVotes()`. This implementation does not provide the “strict schema validation” described in the README for security-critical decision inputs. It also lacks a local provenance or authorization boundary for externally supplied votes. ### Attack Path 1. An attacker gains the ability to submit input to an integration t ...[truncated 1536 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Define and enforce a strict schema for every external vote. - Restrict vote values to an explicit enumeration such as `YES`, `NO`, and `REWRITE`. - Require non-empty, authenticated voter identifiers and reject duplicate voters. - Require confidence, reputation, and weight values to be finite numbers within documented bounds. - Reject unknown fields and malformed nested objects. - Require a non-empty external vote set and establish minimum quorum requirements. - Authenticate or cryptographically verify external vote provenance when votes cross a trust boundary. - Define a strict `constraints` schema and require boolean values for every supported policy option. - Obtain mandatory test and security constraints from trusted board policy instead of accepting policy weakening from the request. - Reject missing mandatory constraints rather than applying permissive defaults. - Add adversarial tests for forged identities, duplicate votes, invalid vote enums, `NaN` or infinite values, empty arrays, malformed records, unknown constraint fields, and omitted mandatory policy gates. ]]>
Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (5)

Known Vulnerable Dependency: fast-uri==3.1.0 — 7 advisory(ies): CVE-2026-13676 (fast-uri vulnerable to host confusion via failed IDN canonicalization); CVE-2026-18446 (fast-uri vulnerable to host confusion via backslash authority introducer); CVE-2026-75975 (fast-uri vulnerable to server-side request forgery via malformed IPv6 normalizat) +4 more

High
Category
Supply Chain
Confidence
82% confidence
Finding
The lockfile includes fast-uri 3.1.0, which is reported with multiple URI parsing and canonicalization issues including host confusion and potential SSRF-relevant edge cases. This is a true dependency risk if any component uses fast-uri for security-sensitive URL validation, allowlisting, or request routing, and the merge-governance skill context increases concern because automation tooling may process untrusted repository or network metadata.

Known Vulnerable Dependency: esbuild==0.27.3 — 1 advisory(ies): GHSA-g7r4-m6w7-qqqr (esbuild allows arbitrary file read when running the development server on Window)

Low
Category
Supply Chain
Confidence
91% confidence
Finding
The lockfile pins esbuild to 0.27.3, and the cited advisory affects esbuild's development server on Windows by allowing arbitrary file read in a specific runtime configuration. This is a real supply-chain risk, but in this skill's context it appears to be a build/runtime tool dependency rather than evidence of malicious code, so exploitability depends on whether the skill actually launches esbuild's dev server on Windows.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"demo": "node --import tsx run.js --input ./examples/input.json"
  },
  "dependencies": {
    "consensus-guard-core": "^1.1.15",
    "tsx": "^4.20.3"
  },
  "license": "MIT",
Confidence
89% confidence
Finding
The dependency uses a caret range, which allows newer compatible versions to be installed over time. This can introduce supply-chain risk because a compromised or breaking upstream release could be pulled into future installs without explicit review, especially for a security-relevant merge-governance skill.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "consensus-guard-core": "^1.1.15",
    "tsx": "^4.20.3"
  },
  "license": "MIT",
  "engines": {
Confidence
84% confidence
Finding
The tsx dependency is also specified with a caret range, allowing automatic drift to later versions. Although this is common in development tooling, it still creates a supply-chain exposure because the tool is executed directly via node --import tsx in test and demo workflows.

Missing User Warnings

Low
Confidence
77% confidence
Finding
The handler persists a decision via writeArtifact, which is a file or state write operation covered by the missing-warning rule for code files. In this file there is no confirmation prompt, logging, print, docstring, or comment disclosing that persistent board state will be written.

Static analysis

No suspicious patterns detected.