Back to skill

Security audit

Mithilesh

Security checks for vulnerabilities and agentic risk

Overview

ClawGuard is a disclosed governance tool, but it claims session-wide control over all write actions and persists action details with under-disclosed Redis network use.

Install only if you intentionally want a skill to gate all write-capable actions in the agent session. Avoid putting secrets or sensitive personal data in justifications, contain or remove Redis unless you explicitly need it, and require tighter scoping, risk-level validation, retention controls, and pinned dependencies before using it for real governance.

Vulnerability Patterns
  • Skill Instruction HijackingAlters the agent's session goals or safety constraints when the skill loads
  • Insecure Skill Coding PracticesFinds exploitable flaws such as hardcoded secrets or command injection
  • 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)

T01 · Skill Instruction Hijacking

Error
Location
GUIDANCE.md:3
Finding

Global Agent Behavior Hijacking Through Mandatory Governance Instructions

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
index.ts:123
Finding

Malformed or Out-of-Range Risk Levels Bypass Governance Blocking

Content
View full analysis
{ if (await this.velocityTripped(params.action_type)) { await this.logAction(params, 'THROTTLED'); return { allowed: false, message: 'CIRCUIT BREAKER TRIPPED: Too many actions in 60s.' }; } if (params.risk_level >= 5) { await this.logAction(params, 'NEEDS_CIVIC'); return { allowed: false, needs_civic: true, message: 'CRITICAL RISK: Identity verification required via Civic.' }; } if (params.risk_level >= 4) { await this.logAction(params, 'BLOCKED'); return { allowed: false, message: 'Governance Violation: Risk too high.' }; } await this.logAction(params, 'APPROVED'); return { allowed: true, message: 'Action logged.' }; } ``` ```ts evaluate_action: async ({ action_type, justification, risk_level }: EvaluateParams) => { if (!action_type || !justification || typeof risk_level !== 'number') { throw new Error('action_type, justification, and risk_level are required'); } return guard.evaluateAction({ action_type, justification, risk_level }); }, ``` ### Technical Analysis Input validation only verifies that `risk_level` has the JavaScript type `number`. It does not enforce the documented integer range of 1 through 5. Consequently, values such as `-1`, `0`, or fractional values below 4 pass validation and reach the approval branch. JavaScript's `NaN` also has type `number`, while both `NaN >= 5` and `NaN >= 4` evaluate to false. Where the tool invocation layer permits non-finite JavaScript numbers, `NaN` therefore follows the same approval path. The authorization decision is fail-open: every value that does not satisfy either threshold is treated as approved, including malformed values ...[truncated 1246 chars]
Remediation
View remediation
5 ) { throw new Error('risk_level must be an integer from 1 through 5'); } ``` Additional hardening should include: 1. Define `risk_level` as a narrow TypeScript type, such as `1 | 2 | 3 | 4 | 5`, while retaining runtime validation. 2. Reject unknown or malformed fields at the external tool boundary using a schema validator. 3. Use an explicit `switch` over valid risk levels so unexpected values fail closed. 4. Add tests for negative numbers, zero, fractions, `NaN`, positive and negative infinity, strings, null values, and values greater than 5. 5. Consider deriving risk server-side from an action policy rather than trusting the caller's self-assigned classification. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
index.ts:27
Finding

Redis Failures Silently Disable the Velocity Circuit Breaker

Content
View full analysis
=> { if (redisDisabled) { return false; } if (redisReady) { return true; } if (!redisInitPromise) { redisInitPromise = redisClient .connect() .then(() => undefined) .catch((err) => { redisDisabled = true; throw err; }); } try { await redisInitPromise; return redisReady; } catch (err) { logRedisError('Initial connection failed', err); return false; } }; ``` ```ts private async velocityTripped(actionType: string): Promise { const redisAvailable = await ensureRedisReady(); if (!redisAvailable) { return false; } const key = `velocity:${actionType}`; try { const count = await redisClient.incr(key); if (count === 1) { await redisClient.expire(key, 60); } return count > 5; } catch (err) { logRedisError('Velocity check failed', err); return false; } } ``` ### Technical Analysis Both Redis unavailability and Redis command errors are represented by `false`, which is also the normal result for “the velocity threshold has not been exceeded.” The caller therefore cannot distinguish a successful rate-limit check from a failed check. After an initial connection failure, `redisDisabled` is set to true. Subsequent checks immediately return false and do not retry. Errors from `INCR` or `EXPIRE` also return false. As a result, the governance mechanism fails open and silently authorizes continued evaluation when its circuit-breaker backend is unavailable. The fallback URL uses port `6373`, whereas the conventional Redis port is `6379`. This may be intent ...[truncated 1351 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (13)

Intent-Code Divergence

High
Category
Not specified by scanner
Confidence
96% confidence
Finding

The manifest promises that risk_level >= 4 is automatically blocked, but the code handles risk_level >= 5 with a distinct NEEDS_CIVIC path instead of a straight block. This policy mismatch is dangerous because callers may rely on the documented guarantee while the code implements a different control flow that could enable bypasses, confusion, or unsafe escalation handling for the highest-risk actions.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
87% confidence
Finding

The skill describes governance over sensitive operations like SQL, file-system, and API writes, but it does not declare an explicit tool scope such as permissions or allowed-tools. If the runtime infers broader capabilities than intended, the skill may access environment-backed functionality without clear restriction, undermining least-privilege guarantees and making the governance layer itself harder to trust.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill description presents a SQLite-based governance/audit layer, but the implementation also introduces Redis connectivity via environment configuration for throttling. This hidden external dependency expands the skill's capabilities and data flow beyond what users are told, which is dangerous in a governance control because availability and behavior can silently change based on external infrastructure.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

Adding Redis gives the skill outbound network capability that is not necessary for a simple SQLite audit ledger and is not justified in the stated purpose. In a pre-action governance layer, undisclosed network access increases attack surface, creates an exfiltration path for action metadata, and introduces external control over authorization behavior.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
80% confidence
Finding

The code creates a Redis client from REDIS_URL and later sends per-action velocity data derived from action_type over the network. There is logging for Redis errors, but no user-facing notice, comment, or warning that action-related metadata may be sent to Redis.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
85% confidence
Finding

The tool logs action_type, justification, and risk_level into a local SQLite ledger without any explicit disclosure before collection. Because justifications may contain sensitive operational or user-provided content, silent persistence creates privacy and data-retention risk, especially in a governance component that may be invoked frequently and automatically.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

The redis dependency uses a caret range, which permits automatic installation of newer minor/patch releases instead of a single reviewed version. In a security-sensitive governance layer that mediates SQL, file-system, and API writes, this weakens supply-chain control and makes builds less reproducible, increasing the chance of pulling an unintended or vulnerable release.

Content

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

json
"test": "node --loader ts-node/esm tests/manual.ts"
  },
  "dependencies": {
    "redis": "^5.11.0",
    "sqlite": "^5.1.1",
    "sqlite3": "^5.1.7"
  },

Unverifiable Dependency: redis has 1 known advisory(ies) (CVE-2021-29469 (Node-Redis potential exponential regex in monitor mode)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
82% confidence
Finding

The manifest does not pin redis to a single version, so it is not possible to verify from this file alone whether the installed release avoids the known advisory for node-redis. This uncertainty is a genuine supply-chain risk because the package could resolve to an affected version in some environments, though the cited issue appears limited in scope and may depend on monitor-mode usage.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

The sqlite package is specified with a caret range rather than an exact version, so different installations may resolve to different releases. For a component responsible for authorization and audit behavior, non-reproducible dependency resolution can introduce unreviewed code changes or vulnerable transitive states into a trusted control point.

Content

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

json
},
  "dependencies": {
    "redis": "^5.11.0",
    "sqlite": "^5.1.1",
    "sqlite3": "^5.1.7"
  },
  "devDependencies": {

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

The sqlite3 dependency is unpinned, allowing installs to float across compatible releases. Because this skill relies on SQLite for its audit ledger, an unexpected dependency change in a native database binding increases supply-chain risk and may expose the control layer to stability or security issues in a particularly sensitive path.

Content

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

json
"dependencies": {
    "redis": "^5.11.0",
    "sqlite": "^5.1.1",
    "sqlite3": "^5.1.7"
  },
  "devDependencies": {
    "ts-node": "^10.9.2",

Unverifiable Dependency: sqlite3 has 2 known advisory(ies) (CVE-2022-21227 (Denial-of-Service when binding invalid parameters in sqlite3); CVE-2022-43441 (sqlite vulnerable to code execution due to Object coercion)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding

The sqlite3 dependency has known advisories, but because the manifest allows version drift, the actual installed version cannot be confirmed as safe. In this skill, sqlite3 underpins the audit ledger for a governance layer, so unresolved uncertainty around a database binding in a trusted logging/authorization path is more concerning than in a non-security-critical utility.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
88% confidence
Finding

The ts-node devDependency is not pinned, which can make test and development execution non-reproducible. While this is less severe than runtime dependencies, unreviewed tooling changes can still affect build/test integrity and potentially mask or introduce insecure behavior during validation.

Content

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

json
"sqlite3": "^5.1.7"
  },
  "devDependencies": {
    "ts-node": "^10.9.2",
    "typescript": "^5.4.2"
  }
}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
88% confidence
Finding

The typescript devDependency is version-ranged, allowing automatic drift in compiler behavior between environments. Inconsistent tooling can change emitted code or type-checking outcomes, reducing assurance for a security-relevant package that is expected to enforce governance decisions reliably.

Content

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

json
},
  "devDependencies": {
    "ts-node": "^10.9.2",
    "typescript": "^5.4.2"
  }
}

Static analysis

No suspicious patterns detected.