Back to skill

Security audit

Cyber KEV Triage

Security checks for vulnerabilities and agentic risk

Overview

This skill is a narrowly scoped vulnerability triage helper, with some report-output safety issues users should understand before relying on it.

Use this skill for defensive vulnerability prioritization, but choose output paths carefully because reports can overwrite existing files, including when --dry-run is supplied. Treat CSV reports as containing untrusted data if vulnerability or asset fields come from outside your control; JSON or Markdown output is preferable unless the CSV is sanitized before opening in a spreadsheet.

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

Warning
Location
scripts/kev_triage.py:96
Finding

CSV Formula Injection Through Untrusted Vulnerability Fields

Content
View full analysis
dict: cve = str(vuln.get("cve", "UNKNOWN")) cvss = float(vuln.get("cvss", 5.0)) known_exploited = bool(vuln.get("known_exploited", False)) asset = str(vuln.get("asset", "unmapped")) criticality = asset_map.get(asset, "medium") ``` ```python with output_path.open("w", newline="", encoding="utf-8") as handle: writer = csv.DictWriter( handle, fieldnames=[ "priority", "cve", "asset", "cvss", "known_exploited", "criticality", "score", "recommended_due_days", ], ) writer.writeheader() writer.writerows(result["details"]["prioritized_vulnerabilities"]) ``` ### Technical Analysis The `cve` and `asset` fields are read from attacker-controlled JSON input and converted to strings without spreadsheet-specific neutralization. These values are subsequently passed directly to `csv.DictWriter`. CSV quoting only preserves the CSV structure; it does not prevent spreadsheet applications from interpreting cells beginning with characters such as `=`, `+`, `-`, or `@` as formulas. Consequently, an attacker who controls an input field can place a spreadsheet formula in the generated report. Exploitation depends on a user opening the CSV output in a formula-capable spreadsheet application. The Python script itself does not evaluate the formula. ### Attack Path 1. An attacker provides or modifies the triage JSON input. 2. The attacker sets a string field such as `asset` or `cve` to a spreadsheet formula beginning with `=`, `+`, `-`, or `@`. 3. An operator runs the sc ...[truncated 1011 chars]
Remediation
View remediation
object: if not isinstance(value, str): return value if value.lstrip().startswith(DANGEROUS_CSV_PREFIXES): return "'" + value return value rows = [ {key: spreadsheet_safe(value) for key, value in row.items()} for row in result["details"]["prioritized_vulnerabilities"] ] writer.writerows(rows) ``` ]]>

T09 · Insecure Skill Coding Practices

Note
Location
scripts/kev_triage.py:148
Finding

Dry-Run Mode Performs Filesystem Writes Despite No-Side-Effects Contract

Content
View full analysis
argparse.Namespace: parser = argparse.ArgumentParser(description="Prioritize vulnerabilities for patching.") parser.add_argument("--input", required=False, help="Path to JSON input.") parser.add_argument("--output", required=True, help="Path to output artifact.") parser.add_argument("--format", choices=["json", "md", "csv"], default="json") parser.add_argument("--dry-run", action="store_true", help="Run without side effects.") return parser.parse_args() ``` ```python result = { "status": status, "summary": summary, "artifacts": [str(Path(args.output))], "details": { "prioritized_vulnerabilities": scored, "total_assets_mapped": len(asset_map), "dry_run": args.dry_run, }, } render(result, Path(args.output), args.format) return 0 ``` The called function always creates parent directories and writes the output: ```python def render(result: dict, output_path: Path, fmt: str) -> None: output_path.parent.mkdir(parents=True, exist_ok=True) ``` ### Technical Analysis The command-line help explicitly defines `--dry-run` as running “without side effects.” However, `args.dry_run` is only copied into report metadata. It is not used to guard the call to `render()`. As a result, dry-run execution still: - Creates missing parent directories. - Creates a new output file. - Truncates and replaces an existing output file. - Writes the generated report to disk. This violates the documented safety contract and can cause unintended filesystem changes when an operator relies on dry-run mode for validation. ### Attack Path 1. An operator selects an existing or s ...[truncated 937 chars]
Remediation
View remediation
Vulnerability Patterns
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
Findings (3)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
92% confidence
Finding

The skill instructs use of a local script and reference file, implying file read and likely file write capabilities, but it does not declare any explicit tool scope such as permissions or allowed-tools. This creates an authorization gap: an agent may access or modify files beyond the minimally required scope, increasing the chance of unintended data exposure or workspace tampering if the skill is invoked in a broader environment.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The --dry-run flag explicitly promises 'Run without side effects', but the program still writes the output file unconditionally in main() via render(). In automation or CI contexts, operators may rely on dry-run semantics to avoid modifying filesystems, so this mismatch can cause unintended artifact creation or overwrite and undermine operational safety expectations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

This Python file performs filesystem side effects by creating parent directories and writing the output artifact, but the code provides no print/log message, confirmation prompt, or explanatory comment/docstring disclosing that behavior. For a code-file review under SQP-2, file writes are in scope when they lack any visible user disclosure.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.