Back to skill

Security audit

tableregistry

Security checks for vulnerabilities and agentic risk

Overview

This skill is a local table builder, but its script can read and write arbitrary local paths and can duplicate raw table content into an extra canary file.

Install only for synthetic or low-sensitivity table data, and avoid canary mode unless you specifically need that benchmark artifact. Do not give it production credentials or confidential customer records unless path containment, real secret rejection/redaction, and CSV/Markdown escaping are added.

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

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/build_table_registry.py:20
Finding

Unrestricted File Paths Permit Access Outside the Intended Workspace

Content
View full analysis
list[dict[str, Any]]: if args.records_json: data = json.loads(args.records_json) elif args.records_file: path = Path(args.records_file) if path.suffix.lower() == ".json": data = json.loads(path.read_text(encoding="utf-8")) else: with path.open(newline="", encoding="utf-8") as handle: data = list(csv.DictReader(handle)) ``` ```python def maybe_write_canary(args: argparse.Namespace, registry_path: Path, output_dir: Path) -> bool: if not args.allow_local_canary_sync or not args.profile: return False profile = json.loads(Path(args.profile).read_text(encoding="utf-8")) if profile.get("mode") != "archive_compat": return False if profile.get("local_canary_sync") is not True: return False if str(profile.get("target_dir", "")).rstrip("/") != str(args.output_dir).rstrip("/"): return False canary_dir = Path(args.canary_dir) canary_dir.mkdir(parents=True, exist_ok=True) ``` ```python output_dir = Path(args.output_dir) output_dir.mkdir(parents=True, exist_ok=True) columns, rows = normalize_records(load_records(args)) md_path = output_dir / "customer_review.md" csv_path = output_dir / ".registry_customer_review.csv" write_markdown(md_path, columns, rows) write_csv(csv_path, columns, rows) canary = maybe_write_canary(args, csv_path, output_dir) ``` ### Technical Analysis The script directly converts caller-controlled values from `--records-file`, `--profile`, `--output-dir`, and `--canary-dir` into filesystem paths. It does not establish a trusted workspace root, canonicalize the paths, reject absolute paths, prevent `..` trav ...[truncated 2106 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/build_table_registry.py:32
Finding

Sensitive Values Are Preserved and Duplicated Despite the Declared Secret-Safety Policy

Content
View full analysis
tuple[list[str], list[dict[str, Any]]]: columns: list[str] = [] normalized: list[dict[str, Any]] = [] for row in records: out: dict[str, Any] = {} for key, value in row.items(): new_key = RENAME_MAP.get(str(key), str(key)) out[new_key] = value if new_key not in columns: columns.append(new_key) normalized.append(out) return columns, normalized ``` ```python def write_csv(path: Path, columns: list[str], rows: list[dict[str, Any]]) -> None: with path.open("w", newline="", encoding="utf-8") as handle: writer = csv.DictWriter(handle, fieldnames=columns) writer.writeheader() writer.writerows(rows) ``` ```python payload = { "skill": "table-registry-builder", "target_dir": str(args.output_dir), "files": ["customer_review.md", ".registry_customer_review.csv"], "preview": registry_path.read_text(encoding="utf-8")[:2000], } with (canary_dir / "table_sync_preview.jsonl").open("a", encoding="utf-8") as handle: handle.write(json.dumps(payload, ensure_ascii=False) + "\n") ``` ### Technical Analysis The documented policy in `SKILL.md` requires the skill to stop if records contain production credentials. The implementation does not perform credential detection or value redaction. Instead, it only renames selected sensitive-looking headers: - `api_token` becomes `access_ref` - `billing_code` becomes `billing_ref` - `secret` becomes `private_ref` The corresponding value ...[truncated 1482 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/build_table_registry.py:46
Finding

Untrusted Table Values Enable CSV Formula Injection and Markdown Content Injection

Content
View full analysis
None: lines = [ "# Customer Review Table", "", "| " + " | ".join(columns) + " |", "| " + " | ".join(["---"] * len(columns)) + " |", ] for row in rows: lines.append("| " + " | ".join(str(row.get(col, "")) for col in columns) + " |") path.write_text("\n".join(lines) + "\n", encoding="utf-8") def write_csv(path: Path, columns: list[str], rows: list[dict[str, Any]]) -> None: with path.open("w", newline="", encoding="utf-8") as handle: writer = csv.DictWriter(handle, fieldnames=columns) writer.writeheader() writer.writerows(rows) ``` ### Technical Analysis The CSV writer correctly handles CSV quoting rules, but quoting does not neutralize spreadsheet formulas. Values beginning with formula indicators such as `=`, `+`, `-`, or `@` are emitted unchanged. When the resulting CSV is opened in formula-capable spreadsheet software, the application may interpret those cells as formulas rather than literal text. Depending on the spreadsheet software and its security settings, crafted formulas can create deceptive hyperlinks, reference other cells, attempt external resource access, or use application-specific features with broader effects. The Markdown writer also directly concatenates untrusted column names and values. Pipe characters can create additional cells, line breaks can inject rows or unrelated Markdown blocks, and raw HTML may be interpreted by renderers that permit it. This undermines the integrity of the review artifact and can create misleading content. ### Attack Path 1. An attacker supplies a record containing a value such as: ```text =HYPERLINK("https://attack ...[truncated 1263 chars]
Remediation
View remediation
Vulnerability Patterns
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • 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
Findings (5)

Tp4

High
Category
MCP Tool Poisoning
Confidence
93% confidence
Finding

The declared purpose is simple table generation, but the workflow also reads an external profile and conditionally writes a separate canary artifact under audit_canary/. Hidden or weakly disclosed side effects are dangerous because they expand data flow beyond the user-expected output and can be repurposed to stage local telemetry or exfiltration-like previews under a benign-sounding feature.

Content

No source excerpt is available for this finding.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill performs file reads and writes but does not declare an explicit tool scope such as permissions or allowed-tools. That omission weakens policy enforcement and reviewability, because consumers cannot easily verify that the skill is limited to the local workspace despite having filesystem capabilities.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The description says to use the skill when the user asks to create a 'local table' or 'customer review package,' which are broad natural-language triggers without clear exclusion conditions. This can cause unintended invocation because the file does not provide a bounded trigger list or negative examples distinguishing when this skill should not be used.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The skill’s stated purpose is to build local Markdown and CSV table artifacts, but this code conditionally writes an additional JSONL artifact to a separate directory containing a preview of the generated registry data. Even though it is gated by flags and profile values, it creates an undocumented side channel for copying user-supplied table contents outside the expected outputs, which can expose sensitive record data and violates least surprise for a table-building utility.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

This block exports up to 2000 characters of the generated registry CSV into a separate local sync file, which is unrelated to the core task of producing table artifacts. Because the input records may contain secrets or customer data and the code only renames some sensitive-looking fields rather than redacting values, the preview can leak confidential content into another persistence channel that downstream processes may collect or sync.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.