Back to skill

Security audit

Citation Anchoring

Security checks for vulnerabilities and agentic risk

Overview

The skill is advertised as a small no-network citation checker, but the package includes broad research-pipeline tooling that can run scripts and persistently change workspace files, approvals, and reports.

Review this carefully before installing. Install only if you actually want the larger research-pipeline toolkit, not just a citation-drift checker, and run it in an isolated workspace with explicit filesystem and network limits. Do not rely on the stated no-network or analysis-only description without additional runtime controls, and avoid auto-approval or broad routing until the skill is split or re-declared accurately.

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
tooling/quality_gate.py:5565
Finding
Unvalidated Workspace-Relative Paths Allow Out-of-Scope Local File Reads<![CDATA[ ## Vulnerability Details **File Location**: `tooling/quality_gate.py:5565-5594` **Vulnerability Type**: Path traversal and unrestricted local file access **Risk Level**: Medium ### Vulnerable Code ```python def _check_citation_anchoring(workspace: Path, outputs: list[str]) -> list[QualityIssue]: from tooling.common import read_jsonl draft_rel = outputs[0] if outputs else "output/DRAFT.md" baseline_rel = "output/citation_anchors.prepolish.jsonl" baseline_path = workspace / baseline_rel draft_path = workspace / draft_rel if not baseline_path.exists(): return [] if not draft_path.exists(): return [] baseline_records = [r for r in read_jsonl(baseline_path) if isinstance(r, dict)] baseline_map: dict[str, set[str]] = {} for rec in baseline_records: if str(rec.get("kind") or "").strip() != "h3": continue title = str(rec.get("title") or "").strip() keys = rec.get("cite_keys") or [] if not title or not isinstance(keys, list): continue baseline_map[title] = set(str(k).strip() for k in keys if str(k).strip()) if not baseline_map: return [ QualityIssue( code="citation_anchors_empty", message=f"`{baseline_rel}` exists but has no H3 citation anchors; delete it and rerun `draft-polisher` to regenerate a baseline.", ) ] draft_text = draft_path.read_text(encoding="utf-8", errors="ignore") ``` ### Technical Analysis The first value in `outputs` is treated as a workspace-relative draft path and joined directly with `workspace`. The implementation does not reject: - Absolute paths. - Paths containing `..` components. - Symlinks that resolve outside the workspace. - Other path forms that escape the intended workspace boundary. With `pathlib`, joining a base path to an absolute path discards the base path. For example, `workspace / Path("/tmp/target")` resolves to ...[truncated 1914 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Introduce a centralized workspace-containment helper: ```python def resolve_workspace_path(workspace: Path, relpath: str) -> Path: root = workspace.resolve() supplied = Path(relpath) if supplied.is_absolute(): raise ValueError("Absolute paths are not permitted") candidate = (root / supplied).resolve(strict=False) if not candidate.is_relative_to(root): raise ValueError("Path escapes the workspace") return candidate ``` 2. Replace the vulnerable join with validated resolution: ```python draft_path = resolve_workspace_path(workspace, draft_rel) baseline_path = resolve_workspace_path(workspace, baseline_rel) ``` 3. If the referenced file must already exist, resolve it with `strict=True` and verify containment after symlink resolution. 4. Reject empty paths, null bytes, unexpected file types, and directories. 5. Restrict the anchoring checker to the declared fixed inputs where caller-selected alternatives are not necessary: ```python draft_path = resolve_workspace_path(workspace, "output/DRAFT.md") baseline_path = resolve_workspace_path( workspace, "output/citation_anchors.prepolish.jsonl" ) ``` 6. Apply the same containment validation to every workspace-derived read and write path in `tooling/executor.py`, `tooling/common.py`, and `tooling/quality_gate.py`. 7. Add tests covering absolute paths, `../` traversal, nested traversal, symlink escapes, valid workspace paths, and malformed output entries. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
tooling/quality_gate.py:584
Finding
Citation-Anchoring Checks Fail Open When Invoked Under the Exported Skill Name<![CDATA[ ## Vulnerability Details **File Location**: `tooling/quality_gate.py:584-687` **Vulnerability Type**: Missing security-check dispatch and fail-open validation **Risk Level**: Low ### Vulnerable Code ```python def check_unit_outputs(*, skill: str, workspace: Path, outputs: list[str]) -> list[QualityIssue]: if skill == "idea-brief": return _check_idea_brief(workspace, outputs) if skill == "literature-engineer": return _check_literature_engineer(workspace, outputs) if skill == "arxiv-search": return _check_arxiv_search(workspace, outputs) # Other skill-specific branches omitted here. if skill == "prose-writer": return _check_draft(workspace, outputs) if skill == "draft-polisher": issues = _check_draft(workspace, outputs) issues.extend(_check_citation_anchoring(workspace, outputs)) return issues if skill == "global-reviewer": return _check_global_review(workspace, outputs) if skill == "pipeline-auditor": return _check_audit_report(workspace, outputs) if skill == "latex-scaffold": return _check_latex_scaffold(workspace, outputs) if skill == "latex-compile-qa": return _check_latex_compile_qa(workspace, outputs) if skill == "artifact-contract-auditor": return _check_contract_report(workspace, outputs) if skill == "protocol-writer": return _check_protocol(workspace, outputs) if skill == "tutorial-spec": return _check_tutorial_spec(workspace, outputs) if skill == "idea-signal-mapper": return _check_idea_signal_table(workspace, outputs) if skill == "idea-direction-generator": return _check_idea_direction_pool(workspace, outputs) if skill == "idea-screener": return _check_idea_screening_table(workspace, outputs) if skill == "idea-shortlist-curator": return _check_idea_shortlist(workspace, outputs) if skill == "idea-memo-writer": return _ ...[truncated 2258 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Add an explicit dispatcher branch for the exported Skill: ```python if skill == "citation-anchoring": return _check_citation_anchoring(workspace, outputs) ``` 2. Avoid treating unknown skill names as successful validation. Return a structured issue or raise a controlled exception: ```python return [ QualityIssue( code="unsupported_quality_gate_skill", message=f"No quality gate is registered for skill `{skill}`.", ) ] ``` 3. Distinguish skipped, failed, and passed states. A missing baseline should produce a clear `SKIPPED` or configuration result, not an empty list indistinguishable from PASS. 4. Treat a missing current draft as an error because no comparison can be performed. 5. Implement the report promised by `SKILL.md`, including: - Explicit `Status: PASS` only after a completed comparison with no drift. - Explicit `Status: FAIL` for added, removed, or migrated citation keys. - Explicit `Status: SKIPPED` when the baseline is absent. - A concise subsection diff table and representative examples. 6. Add regression tests for: - Invocation with `skill="citation-anchoring"`. - No citation drift. - Citations added or removed within an H3 subsection. - Citations migrated across H3 subsections. - Missing and empty baselines. - Missing drafts. - Renamed or removed H3 headings. - Unknown skill names. ]]>
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 (43)

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The description claims a narrow, analysis-only citation anchoring regression check: confirm citations did not migrate across `###` subsections, with no editing. The supplied code does not implement citation anchoring or subsection drift detection at all. Instead, it is a generic common utilities module supporting many unrelated workflow operations. Critically, it includes substantial content- and file-modifying behavior such as atomic_write_text, backup_existing, write_jsonl, write_tsv, dump_yaml, copy_tree, update_status_log, update_status_field, ensure_decisions_approval_checklist, set_decisions_approval, upsert_checkpoint_block, and seed_queries_from_topic. Those are materially inconsistent with the declared guardrail of analysis-only/no edits. The primary purpose and capabilities are therefore significantly different from the declared description.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description is for a narrow, analysis-only citation anchoring regression check. The supplied code does not inspect citations, subsections, or baseline anchor files at all. Instead, it is a general-purpose execution/orchestration module for pipeline units: it finds runnable tasks, marks them DOING/DONE/BLOCKED, can auto-approve human checkpoints, invokes `scripts/run.py` through `subprocess.run`, validates required outputs, writes logs and error summaries, updates status fields, and enforces quality-gate and section-first cutover constraints. This is a materially different primary purpose and includes undeclared capabilities—especially execution of other scripts and modification of multiple workspace files—directly conflicting with the stated guardrail of analysis-only/no edits.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description is for a narrow verification utility that checks whether citations drifted across `###` subsections after edits and explicitly says analysis-only with no content edits. The supplied code does nothing about citations, anchors, subsection comparisons, regression checking, or baseline anchor files. Instead, it is a substantial ideation/report-generation module for research-direction synthesis and prioritization. It reads and validates ideation contracts, parses idea briefs, analyzes literature notes/taxonomies, constructs signals and direction cards, scores them, and emits multiple written artifacts to disk. That is a materially different primary purpose and includes undeclared write/edit behavior, so this is a clear mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description is about checking citation anchoring stability in document subsections and explicitly says it is analysis-only and used after polishing edits. The supplied code does nothing related to citations, subsections, regression checks, or claim→evidence alignment. Instead, it implements a pipeline configuration loader/parser for `.pipeline` files, including filesystem reads, YAML parsing, schema validation, variant inheritance resolution, and deep-merge behavior. This is a materially different primary purpose and includes undeclared capabilities unrelated to the stated skill.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The description is narrowly scoped to a regression check for citation anchoring within `###` subsections after polishing, explicitly analysis-only and no network. The supplied code is instead a broad workspace quality-gate framework. It defines many validators for unrelated skills and artifacts, dispatches on many skill names, and persists results by writing/appending `output/QUALITY_GATE.md`. While one path references citation anchoring for `draft-polisher`, that is only a small subset of the module’s behavior. The primary purpose, scope, and side effects are materially broader than declared, so this is a clear mismatch.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description is narrow: a regression check for whether citations stayed within the same `###` subsection after editing, analysis-only, and specifically dependent on a baseline anchor file. The supplied code does not implement that primary behavior. Instead, it is part of a large `quality_gate.py` system that reads and validates many different workspace artifacts and section files, enforcing schema, completeness, writing quality, citation hygiene, evidence density, mapping consistency, and report PASS/fail conditions. While some parts touch anchors/citations, they are not focused on comparing citation positions against a baseline to detect subsection migration. The code also accesses many files beyond an anchor baseline (outline, sections, citations, output reports, tables, visuals, etc.). Therefore the actual behavior is materially broader and different from the declared purpose.

Tp4

High
Category
MCP Tool Poisoning
Confidence
99% confidence
Finding
The declared description is narrowly scoped: a regression-only, analysis-only check for whether citations migrated across `###` subsections after polishing. The supplied code chunk does contain that functionality in `_check_citation_anchoring`, which reads a baseline anchor file and compares per-H3 citation sets. However, the overall code chunk is much broader in purpose. It adds numerous unrelated quality-gate behaviors, including citation-style heuristics, repeated template text detection, missing section checks, subsection length/citation density enforcement, protocol/tutorial/global-review validation, LaTeX scaffold and compile QA, and PDF content/page-count inspection. Those are materially different capabilities from the declared purpose. Network behavior appears consistent with the declaration (`none`), and the code is analysis-oriented rather than editing, but the primary scope is significantly under-declared.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
The file content is a full arXiv survey pipeline with multi-stage retrieval, drafting, polishing, and artifact production, which does not match the declared citation-anchoring analysis-only skill purpose. This kind of skill/manifest mismatch is dangerous because it can cause users or orchestrators to invoke a far more powerful workflow than intended, bypassing trust and review assumptions tied to the advertised narrow function.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The file exposes broad drafting and content-modification capabilities, including writing, polishing, merging, and citation injection, despite the surrounding skill context claiming analysis-only behavior. In a routed agent system, this can lead to unauthorized content generation or modification when a user expected a narrow audit step, expanding both operational risk and the chance of policy bypass.

Scope Creep

High
Confidence
98% confidence
Finding
The documentation explicitly allows online metadata enrichment even though the skill metadata says network access is none. A false no-network declaration is dangerous because it breaks sandboxing and trust assumptions, potentially causing unexpected outbound requests, data leakage, or policy violations in restricted environments.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The file's Stage 5 workflow explicitly performs writing, polishing, citation injection, merging, and iterative redrafting, directly contradicting the manifest guardrail of 'analysis-only; do not edit content.' This contradiction is especially dangerous because users may authorize the skill based on its guardrail while the actual pipeline can alter substantial portions of the workspace.

Description-Behavior Mismatch

High
Confidence
97% confidence
Finding
This module provides extensive filesystem mutation and workflow-management capabilities that far exceed the stated scope of a citation-anchoring skill. In an agent setting, this scope mismatch is dangerous because a skill advertised as analysis-only can be invoked with higher trust while still being able to rewrite status files, decision logs, queries, YAML/JSONL/TSV artifacts, copy trees, and rename existing files across the workspace.

Intent-Code Divergence

High
Confidence
99% confidence
Finding
The declared guardrail says the skill is analysis-only and must not edit content, yet the module contains direct write and rename primitives such as atomic_write_text and backup_existing. That contradiction is especially risky in agent environments because operators may rely on the manifest for safety expectations, while the implementation still has the capability to modify or replace files.

Context-Inappropriate Capability

High
Confidence
95% confidence
Finding
The query seeding, pipeline resolution, approval checklist manipulation, and workflow state management logic is unrelated to citation anchoring and materially expands what the skill can influence. In practice this creates an overprivileged component that can alter research workflow inputs and approval state, enabling integrity loss or policy bypass if the skill is triggered in the wrong context or intentionally abused.

Description-Behavior Mismatch

High
Confidence
98% confidence
Finding
The file is an active workflow executor that changes unit state, writes logs, updates approvals, and launches scripts, which materially conflicts with the declared skill purpose of analysis-only citation anchoring. In a system that routes permissions based on skill metadata, this mismatch can grant hidden execution and mutation capability under a benign-looking skill label.

Scope Creep

High
Confidence
98% confidence
Finding
The code updates `UNITS.csv`, `STATUS.md`, and `DECISIONS.md`, changing workflow state despite the manifest claiming analysis-only, do-not-edit behavior. That mismatch is dangerous because operators may grant the skill access assuming no side effects, while the implementation can alter approvals and execution status in the workspace.

Context-Inappropriate Capability

High
Confidence
97% confidence
Finding
A citation-stability checker should only read and compare documents, but this code constructs and runs an external Python script with workspace-controlled parameters. That introduces arbitrary code-execution capability into a context whose users and policy may assume is non-executing, greatly increasing the blast radius if the workspace or repo content is untrusted.

Description-Behavior Mismatch

High
Confidence
99% confidence
Finding
This file implements a large ideation and report-generation pipeline that is unrelated to the declared skill purpose of citation-anchor regression checking. In an agent skill, capability drift like this is dangerous because it expands behavior far beyond user expectations, increasing the chance of unauthorized file processing, misleading outputs, and misuse of the skill as a general research-generation tool.

Scope Creep

High
Confidence
98% confidence
Finding
The skill metadata says the tool is analysis-only and should not edit content, but this file contains generic write helpers that create directories and write JSON, JSONL, and Markdown artifacts to disk. Even if intended for reporting, undisclosed write capability violates the guardrail and can modify workspace state, which is especially risky in automated agent environments where users rely on non-mutating behavior.

Context-Inappropriate Capability

High
Confidence
98% confidence
Finding
The code from this region performs research-direction scoring, ranking, memo generation, appendix generation, and prioritization logic—capabilities that are unjustified for a citation-drift regression checker. In context, this is dangerous because it turns a narrowly trusted verification skill into a broad content-generation system, undermining least privilege and making misuse or accidental data processing much more likely.

Lp3

Medium
Category
MCP Least Privilege
Confidence
82% confidence
Finding
The skill declares no explicit tool scope while its documented workflow requires reading inputs and writing a report, and the metadata also indicates dependence on Python. That gap creates unnecessary authority and makes it harder for a runtime or reviewer to constrain the skill to the minimum required file operations, increasing the chance of unintended or abusive file and shell access.

Vague Triggers

Medium
Confidence
91% confidence
Finding
Broad routing hints such as survey, review, and literature review can cause this pipeline to activate for many generic requests beyond its intended use. In the context of a mismatched and overpowered skill, overbroad routing materially increases the chance of unintended invocation and misuse.

Vague Triggers

Medium
Confidence
90% confidence
Finding
Setting the pipeline as the default route without clear activation constraints creates ambiguous invocation scope and increases the likelihood that it runs in contexts where it is not appropriate. Because the pipeline is expansive and write-capable, accidental default selection can have significant downstream effects on workspace state and user expectations.

Context-Inappropriate Capability

Medium
Confidence
96% confidence
Finding
The pipeline includes retrieval, PDF extraction, evidence banking, and full-text processing that are unrelated to subsection-level citation drift regression checks. While these functions are not inherently malicious, bundling them into a supposedly narrow anchoring skill increases attack surface, expands data handling scope, and makes unintended execution more damaging.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The file frames the pipeline as a Chinese thesis workflow from the title onward, and repeatedly requires Chinese thesis writing and polishing behavior. Because the description does not offer opt-in, language choice, or a clearly stated region/compliance justification, it creates a natural-language locale policy concern under the rule.

Static analysis

No suspicious patterns detected.