Back to skill

Security audit

Artifact Redactor

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly does what it says, but it can unintentionally read files outside the selected artifact folder through symlinks and include that data in a share bundle.

Install only if you will run it on artifact trees you control and have checked for symlinks. Do not treat its output as automatically safe for public sharing; manually review skipped files, symlinks, and the generated Markdown report before sharing.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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
Findings (2)

T05 · Unauthorized Access and Privilege Escalation

Error
Location
scripts/redaction_common.py:63
Finding

Symbolic Links Can Escape the Selected Artifact Root

Content
View full analysis
list[Path]: if root.is_file(): return [root] return [path for path in sorted(root.rglob("*")) if path.is_file()] def is_text_candidate(path: Path) -> bool: if path.suffix.lower() in TEXT_SUFFIXES: return True return path.name.lower() in {"license", "readme", ".gitignore"} def read_text_if_supported(path: Path) -> Optional[str]: if not is_text_candidate(path): return None try: return path.read_text(encoding="utf-8") except (UnicodeDecodeError, OSError): return None ``` The redaction workflow subsequently copies the content returned by this helper: ```python for path in iter_candidate_files(root): if out_dir.exists() and path_is_within(path, out_dir): continue rel_path = Path(path.name) if root.is_file() else path.relative_to(root) text = read_text_if_supported(path) if text is None: skipped_files.append({"file": str(rel_path), "reason": "binary-or-unsupported"}) continue redacted, counts = redact_text(text) merge_counts(totals, counts) target_path = out_dir / rel_path target_path.parent.mkdir(parents=True, exist_ok=True) target_path.write_text(redacted, encoding="utf-8") ``` ### Technical Analysis `Path.rglob()` discovers entries beneath the supplied root, but `Path.is_file()` and `Path.read_text()` follow symbolic links. The implementation does not reject symbolic links or resolve each candidate and verify that its resolved target remains inside the resolved artifact root. Consequently, an artifact directory can cont ...[truncated 2176 chars]
Remediation
View remediation
list[Path]: if root.is_symlink(): return [] if root.is_file(): return [root] return [ path for path in sorted(root.rglob("*")) if not path.is_symlink() and path.is_file() ] ``` 2. Independently resolve and validate every candidate immediately before reading it: ```python resolved_root = root.resolve(strict=True) resolved_candidate = path.resolve(strict=True) try: resolved_candidate.relative_to(resolved_root) except ValueError: # Record as a rejected link requiring manual review. continue ``` 3. Perform validation as close as possible to the file-open operation to reduce time-of-check/time-of-use exposure. Where supported, use non-following file-open facilities such as `os.open()` with `O_NOFOLLOW`, then read from the validated descriptor. 4. Apply the same policy when `--root` itself is a file, because the supplied root can also be a symbolic link. 5. Record rejected links as manual-review items rather than silently processing or omitting them. 6. Add tests covering links to files outside the root, links to directories, broken links, nested links, and replacement of a validated file with a link during processing. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/render_redaction_report.py:69
Finding

Unescaped Artifact Data Is Injected into the Generated Markdown Report

Content
View full analysis
Remediation
View remediation
str: text = str(value or "").replace("\r", " ").replace("\n", " ") for char in ("\\", "`", "*", "_", "{", "}", "[", "]", "(", ")", "#", "+", "-", ".", "!", "|", ">", "<"): text = text.replace(char, "\\" + char) return text ``` 3. Use a dedicated helper for values placed inside inline-code spans. Dynamically choose a backtick fence longer than any backtick sequence in the value, or avoid inline-code formatting for untrusted fields. 4. Validate input JSON against a strict schema, including expected object types, bounded string lengths, numeric line fields, and enumerated severity/code values. 5. Configure the intended Markdown rendering environment to disable raw HTML and automatic remote-resource loading where possible. 6. Add tests using filenames and snippets containing newlines, backticks, headings, links, images, raw HTML, and closing-format delimiters. Verify that each payload appears only as inert text. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • 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
Findings (4)

Tp4

High
Category
MCP Tool Poisoning
Confidence
95% confidence
Finding

The declared purpose says the skill redacts sensitive text in artifacts before sharing. The supplied code chunk only renders a markdown report from already-produced JSON outputs. It loads scan/redaction/check JSON, computes a recommendation, summarizes counts and findings, and writes a markdown file. There is no code here that scans or redacts Markdown, JSON, logs, URLs, paths, secrets, or PII. While reporting could be a supporting component of a larger redaction skill, this specific code chunk’s actual behavior is materially different from the declared primary purpose, so it should be flagged as a mismatch.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The declared description says the skill redacts sensitive content from Markdown, JSON, logs, and other text artifacts before sharing. This code chunk only detects likely sensitive text by iterating candidate files, reading supported text, collecting line-based findings via helper functions, and emitting a JSON report. It does not rewrite, sanitize, or redact the input files or output redacted versions. The behavior is related to sensitive-text handling, but the primary purpose is materially different: auditing/scanning rather than redaction.

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 · scripts/redaction_common.py (reported line 16)May include surrounding context.

python
".cfg",
    ".conf",
    ".csv",
    ".env",
    ".html",
    ".ini",
    ".json",

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
70% confidence
Finding

Without declared permissions the skill's intent is opaque and cannot be validated.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.