Back to skill

Security audit

consensus-guard-core

Security checks for vulnerabilities and agentic risk

Overview

The package appears purpose-built for consensus guard workflows, but review is warranted because its core approval logic and local state write boundaries are not sufficiently constrained for security infrastructure.

Install only after reviewing it as security infrastructure, not a simple helper library. Pin and audit dependencies, validate vote objects and policy thresholds before calling aggregateVotes, never pass user-controlled state paths directly to board write helpers, and run it as a non-root user with a dedicated consensus state directory.

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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
src/policy.mjs:1
Finding

Unvalidated Vote Weights and Thresholds Can Manipulate Consensus Decisions

Content
View full analysis
0) { yes/=total; no/=total; rewrite/=total; } const threshold = Number(policy.approve_threshold ?? 0.7); let final_decision = 'BLOCK'; const rationale = []; if (hard_block) { final_decision='BLOCK'; rationale.push('hard_block detected'); } else if (yes>=threshold) { final_decision='APPROVE'; rationale.push('approval threshold met'); } else if (rewrite>0) { final_decision='REWRITE'; rationale.push('fixable issues exist'); } else { final_decision='BLOCK'; rationale.push('insufficient approval'); } return { method: policy.method || 'WEIGHTED_APPROVAL_VOTE', weighted_yes:+yes.toFixed(6), weighted_no:+no.toFixed(6), weighted_rewrite:+rewrite.toFixed(6), hard_block, rationale, final_decision }; } ``` ### Technical Analysis `aggregateVotes()` converts caller-supplied values with `Number()` but does not verify that they are finite, non-negative, or within the expected range. The function also does not validate the vote array, supported vote values, duplicate persona votes, or the approval threshold. Consequently, inputs such as negative weights, `Infinity`, `NaN`, or thresholds outside the `[0,1]` interval can invalidate the aggregation model. A particularly direct bypass is an approval threshold below zero: with no affirmative voting weight, `yes` remains zero, and `0 >= -1` evaluates to true. The package's `rejectUnknown()` helper only rejects unknown property names and is not in ...[truncated 1378 chars]
Remediation
View remediation
1) { throw new RangeError('approve_threshold must be a finite number in [0,1]'); } for (const vote of votes) { if (!['YES', 'NO', 'REWRITE'].includes(vote.vote)) { throw new TypeError('unsupported vote value'); } const weight = Number(vote.reputation_before); if (!Number.isFinite(weight) || weight < 0 || weight > 1) { throw new RangeError('reputation_before must be a finite number in [0,1]'); } } ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
src/board.mjs:8
Finding

Public Board Write APIs Bypass the State-Path Confinement Helper

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
src/state-path.mjs:24
Finding

Lexical State-Path Validation Does Not Prevent Symlink Escape

Content
View full analysis
/attacker-selected/writable-directory ``` 3. The application resolves a state path such as: ```text redirect/state.json ``` 4. The lexical result begins with the configured root, so the check accepts it. 5. A subsequent board or storage operation opens the returned path. 6. The operating s ...[truncated 872 chars]
Remediation
View remediation
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (8)

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
91% confidence
Finding

The lockfile includes fast-uri 3.1.0, which has multiple advisories involving URI parsing ambiguities such as host confusion and malformed authority handling. If this library is used in security-sensitive URL validation, allowlisting, redirect handling, or SSRF protections, attackers may bypass checks and reach unintended internal or external targets.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · tests/core.test.mjs (reported line 26)May include surrounding context.

js
});

test('resolveStatePath confines traversal to state root', ()=>{
  const out = resolveStatePath({ stateRoot: '.consensus-test', statePath: '../../etc/passwd' });
  const root = new URL('../.consensus-test/', import.meta.url).pathname;
  assert.equal(out.startsWith(root), true);
  assert.equal(out.endsWith('.json'), true);

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
86% confidence
Finding

The inline comment describes this section as "consensus-interact contract wrappers (single boundary for skill tool calls)", which suggests simple wrapper access around board interactions. However, these exports do more than passive reads: writeDecision/writeArtifact create jobs, submit artifacts, and resolve them through the consensus engine, giving this module active mutation and workflow-control capability. With no manifest purpose available to justify broader authority, this is an unjustified capability relative to the stated in-code role.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
82% confidence
Finding

The comment at L42 frames the following exports as "contract wrappers" and a "single boundary for skill tool calls," which implies a thin interface layer. In practice, writeArtifact performs substantive side effects by posting a job, submitting data, and resolving the job, so the documentation understates and mischaracterizes the behavior. This is more than incomplete documentation because the comment suggests abstraction-only wrapping while the code actively mutates board state.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The writeDecision/writeArtifact path creates, submits, and resolves jobs, which persists new decision or artifact data to storage. The code performs these state-changing writes without any confirmation prompt, user-visible logging, or explanatory comment/docstring warning about the mutation.

Content

No source excerpt is available for this finding.

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
93% confidence
Finding

The lockfile pins esbuild to 0.27.3, which is flagged for GHSA-g7r4-m6w7-qqqr. This issue affects esbuild's development server on Windows and can permit arbitrary file reads in that specific usage mode; while package-lock.json alone does not prove the dev server is exposed, the vulnerable version is present and therefore the finding is valid as a dependency risk.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
89% confidence
Finding

The dependency uses a caret range, which allows newer compatible versions to be installed over time rather than a single reviewed version. This creates supply-chain risk: a compromised upstream release or an unexpectedly breaking change could be pulled into the skill without explicit review, especially significant here because this is a core package for an agent skill.

Content

Scanner excerpt · package.json (reported line 20)May include surrounding context.

json
"test": "node --test --import tsx"
  },
  "dependencies": {
    "@consensus-tools/consensus-tools": "^0.2.0",
    "tsx": "^4.20.3"
  },
  "license": "MIT",

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
88% confidence
Finding

The tsx dependency is also version-ranged with a caret, permitting automatic drift to later releases. Even though it is referenced in the test script, dependency confusion or a malicious upstream update could affect development and CI environments, which can still compromise the package release process or developer systems.

Content

Scanner excerpt · package.json (reported line 21)May include surrounding context.

json
},
  "dependencies": {
    "@consensus-tools/consensus-tools": "^0.2.0",
    "tsx": "^4.20.3"
  },
  "license": "MIT",
  "engines": {

Static analysis

No suspicious patterns detected.