Back to skill

Security audit

Aps Filesystem Agent

Security checks for vulnerabilities and agentic risk

Overview

The skill has a coherent APS knowledge-base purpose, but its documented write and commit flows are too broad and could persist or modify sensitive local business data unexpectedly.

Install only if you are comfortable with an APS agent maintaining a local, Git-backed memory of customer rules and scheduling decisions. Before using it with real customer data, tighten path validation, restrict Git staging to approved files, avoid storing raw conversation quotes or secrets, define retention/deletion rules, and use a pinned dependency environment.

Vulnerability Patterns
  • Insecure DependenciesIntroduces malicious components through unsafe dependency sources
  • 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 (5)

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:229
Finding
Unrestricted Proposal Path Allows Arbitrary JSON File Processing and Deletion<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:229-239` **Vulnerability Type**: Path traversal and arbitrary file deletion **Risk Level**: High ### Vulnerable Code ```python def confirm_proposal(proposal_file: str, confirmed_by: str): """Move a proposal from pending_review into the live knowledge base.""" kb = pathlib.Path("aps_knowledge_base") proposal_path = pathlib.Path(proposal_file) proposal = json.loads(proposal_path.read_text()) if proposal.get("type") == "client_memory_update": _apply_memory_update(proposal, confirmed_by) else: _apply_rule(proposal, confirmed_by) # Remove from pending proposal_path.unlink() ``` ### Technical Analysis `proposal_file` is converted directly into a `Path` without verifying that it belongs to `aps_knowledge_base/pending_review`. The path can therefore be absolute or contain `..` traversal components. The selected file is parsed and processed as a proposal, after which `unlink()` deletes it. A symlink placed in the proposal directory could create a similar boundary violation unless symlink handling is explicitly restricted. Schema and proposal-state validation are also absent, so an arbitrary JSON document with compatible fields can be applied to the live knowledge base before deletion. ### Attack Path 1. An attacker supplies or influences a `proposal_file` value referencing an arbitrary readable JSON file, such as an absolute path or a path containing `../`. 2. The attacker induces the user to provide nominal approval for the proposal, or the caller otherwise invokes `confirm_proposal()`. 3. The function reads the external file and interprets its contents as a memory update or rule. 4. The parsed content may be written into the live knowledge base. 5. `proposal_path.unlink()` deletes the externally selected JSON file. ### Impact Assessment An attacker can delete any JSON file writable by the Agent process. Depending on the selected content, the attack ...[truncated 215 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Resolve both the trusted proposal directory and supplied path with `Path.resolve(strict=True)`. - Require the resolved proposal path to be a direct child of the resolved `pending_review` directory. - Reject absolute input paths, `..` components, unexpected filename formats, and symbolic links. - Validate the parsed document against a strict proposal schema before applying it. - Verify that the proposal has `status: "proposed"` and has not already been processed. - Delete only the validated in-directory proposal after all updates complete successfully. - Use transactional or failure-safe update handling so partial processing does not corrupt state. Example containment check: ```python pending = (kb / "pending_review").resolve(strict=True) proposal_path = pathlib.Path(proposal_file).resolve(strict=True) if proposal_path.parent != pending or proposal_path.is_symlink(): raise ValueError("Invalid proposal path") ``` ]]>

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:150
Finding
Unvalidated Identifiers Permit Path Traversal and Arbitrary File Overwrite<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:150-172`, `SKILL.md:244-262`, and `SKILL.md:385-394` **Vulnerability Type**: Path traversal and arbitrary file creation or overwrite **Risk Level**: High ### Vulnerable Code ```python def propose_rule(rule_content: dict, source_quote: str, session_id: str): kb = pathlib.Path("aps_knowledge_base") pending = kb / "pending_review" pending.mkdir(exist_ok=True) ts = datetime.datetime.utcnow().strftime("%Y%m%d_%H%M%S") proposal = { **rule_content, "status": "proposed", "metadata": { **rule_content.get("metadata", {}), "created_at": datetime.datetime.utcnow().isoformat() + "Z", "created_by": "ai_agent", "confirmed_by": None, "source_session": session_id, "source_quote": source_quote, "use_count": 0, "confidence": 0.9 } } out_path = pending / f"proposed_{rule_content['id']}_{ts}.json" out_path.write_text(json.dumps(proposal, ensure_ascii=False, indent=2)) ``` ```python def _apply_rule(proposal: dict, confirmed_by: str): rule_type = proposal.get("type", "general") category_map = { "machine_constraint": "machine_rules", "operator_constraint": "operator_rules", "material_constraint": "material_rules", } subdir = category_map.get(rule_type, "machine_rules") dest = kb / f"domain_rules/{subdir}/{proposal['id']}.json" dest.parent.mkdir(parents=True, exist_ok=True) proposal["status"] = "active" proposal["metadata"]["confirmed_by"] = confirmed_by proposal["metadata"]["confirmed_at"] = ( datetime.datetime.utcnow().isoformat() + "Z" ) dest.write_text(json.dumps(proposal, ensure_ascii=False, indent=2)) ``` ```python def log_decision(session_id: str, decision: dict, rules_used: list[str]): log_entry = { "session_id": session_id, "timestamp": datetime.datetime.ut ...[truncated 1917 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Enforce strict identifier formats before any path construction. For example: ```python import re IDENTIFIER = re.compile(r"^[A-Za-z0-9_-]{1,128}$") def validate_identifier(value: str) -> str: if not IDENTIFIER.fullmatch(value): raise ValueError("Invalid identifier") return value ``` - Reject absolute paths, separators, drive prefixes, null characters, and `.` or `..` path components. - Resolve each completed destination and verify it remains below the expected directory with `Path.is_relative_to()` or an equivalent containment check. - Refuse to follow symlinks in writable directories. - Use exclusive creation where overwriting is not expected. - Separate display identifiers from filesystem-generated names; a random UUID can be used as the actual filename. - Apply the same validation consistently to rule IDs, session IDs, problem types, categories, and all future path-derived fields. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:103
Finding
Untrusted Vector-Index Metadata Can Cause Reads Outside the Knowledge Base<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:103-118` **Vulnerability Type**: Path traversal and unauthorized local file read **Risk Level**: Medium ### Vulnerable Code ```python def get_relevant_rules(query: str, top_k: int = 5) -> list[dict]: collection = client.get_collection("domain_rules") results = collection.query( query_texts=[query], n_results=top_k, where={"status": "active"} ) rules = [] for rule_id, meta in zip(results["ids"][0], results["metadatas"][0]): path = kb / meta["file_path"] rules.append(json.loads(path.read_text())) return rules ``` ### Technical Analysis The function treats ChromaDB metadata as trusted and reads `meta["file_path"]` without validation. If the metadata contains an absolute path, Python path joining can discard the `kb` prefix. If it contains traversal components, the resulting path can escape the knowledge-base root. Index metadata may become attacker-controlled through index poisoning, unsafe imports, filesystem tampering, or another vulnerable write path. The function also does not verify that the loaded rule ID matches the result ID or that the resolved file belongs to `domain_rules`. ### Attack Path 1. An attacker inserts or modifies a ChromaDB record whose `file_path` points outside `aps_knowledge_base/domain_rules`. 2. The malicious record is marked `active` and made relevant to a likely semantic query. 3. `get_relevant_rules()` retrieves the record. 4. The function joins and reads the attacker-controlled path without a containment check. 5. The external JSON content enters the Agent's scheduling context and may influence subsequent decisions. ### Impact Assessment The Agent can be induced to read arbitrary JSON files accessible to its process. Data from those files may be exposed in model context or later output. If the selected JSON resembles an APS rule, it can also influence scheduling behavior. The direct scope is read acc ...[truncated 46 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Do not trust persisted index metadata as an authorization source. - Resolve each retrieved path and verify that it is beneath the resolved `aps_knowledge_base/domain_rules` directory. - Reject absolute paths, traversal components, and symlinks that resolve outside the trusted root. - Prefer deriving the rule path from a validated rule ID and trusted category mapping instead of accepting a stored path. - Verify that the loaded document's `id`, status, and expected schema match the queried index record. - Restrict permissions on `.chromadb` so unauthorized users and processes cannot modify index records. Example: ```python rules_root = (kb / "domain_rules").resolve(strict=True) path = (kb / meta["file_path"]).resolve(strict=True) if not path.is_relative_to(rules_root): raise ValueError("Indexed rule path escapes domain_rules") ``` ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:329
Finding
Broad Git Staging Can Commit Unrelated or Sensitive Repository Files<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md:329-337` **Vulnerability Type**: Excessive file staging and unintended sensitive-data retention **Risk Level**: Medium ### Vulnerable Code ```python def _git_commit(item: dict, confirmed_by: str): kb_path = "aps_knowledge_base" item_id = item.get("id", item.get("memory_type", "unknown")) item_type = item.get("type", "update") action = "add" if item.get("status") == "active" else "update" msg = f"{action}: {item_id} {item_type} ({confirmed_by})" subprocess.run(["git", "-C", kb_path, "add", "-A"], check=True) subprocess.run(["git", "-C", kb_path, "commit", "-m", msg], check=True) ``` ### Technical Analysis `git add -A` stages every modified, deleted, and untracked file in the entire knowledge-base repository. The operation is not limited to the files changed by the approved proposal. Although the subprocess call correctly uses an argument array and is not directly vulnerable to shell command injection, its staging scope violates least-change principles. Any sensitive or unrelated file present in the repository can be incorporated into permanent Git history when an otherwise legitimate knowledge update is confirmed. ### Attack Path 1. A sensitive, temporary, or attacker-created file is placed anywhere inside `aps_knowledge_base`. 2. A user confirms an unrelated rule or memory update. 3. `_git_commit()` executes `git add -A`. 4. Git stages both the approved update and all unrelated repository changes. 5. The subsequent commit permanently records those files or deletions in repository history. 6. Anyone with later repository access may recover committed sensitive content, even if the working-tree file is removed. ### Impact Assessment Sensitive local data may be retained in Git history and exposed to every user or system with repository access. Unrelated deletions and modifications may also be committed, damaging audit integrity and making the commit misleading. Thi ...[truncated 131 chars]
Remediation
<![CDATA[ ## Remediation Suggestions - Track the exact files changed by each operation and stage only those paths. - Resolve and validate every staged path against a strict allowlist of knowledge-base directories. - Inspect `git status --porcelain` before committing and abort if unexpected changes are present. - Maintain an appropriate `.gitignore` for secrets, temporary files, ChromaDB runtime data, and local configuration. - Add automated secret scanning before commits. - Separate generated indexes or operational state from auditable source data if they do not need version control. Example: ```python subprocess.run( ["git", "-C", kb_path, "add", "--", relative_rule_path, "domain_rules/_index.json"], check=True, ) ``` ]]>

T08 · Insecure Dependencies

Note
Location
references/scripts.md:24
Finding
Unpinned ChromaDB Installation Instruction Creates Supply-Chain Risk<![CDATA[ ## Vulnerability Details **File Location**: `references/scripts.md:24-30` **Vulnerability Type**: Unpinned third-party dependency **Risk Level**: Low ### Vulnerable Code ```python def rebuild(kb_path: str = "aps_knowledge_base"): try: import chromadb except ImportError: print("chromadb not installed. Run: pip install chromadb") sys.exit(1) ``` ### Technical Analysis The recommended command installs the latest available `chromadb` release and its transitive dependencies without a version pin or integrity hashes. The exact code installed can therefore change over time without any change to the audited Skill. The package name is legitimate and the audit found no evidence of deliberate dependency confusion, typosquatting, or a malicious source. Nevertheless, the instruction creates avoidable supply-chain and reproducibility risk because future, compromised, or incompatible releases would be accepted automatically. ### Attack Path 1. A user encounters the missing-module message. 2. The user follows the displayed `pip install chromadb` instruction. 3. The package resolver downloads the current release and its transitive dependencies. 4. Installation hooks or subsequently imported package code execute with the user's environment privileges. 5. If the resolved dependency version is compromised or unexpectedly incompatible, the knowledge-base environment is affected. ### Impact Assessment A compromised dependency could execute code with the privileges of the user performing installation or running the rebuild script. Under normal conditions, the more likely impact is non-reproducible behavior or breakage from an incompatible update. No currently malicious dependency was identified in the reviewed files. ]]>
Remediation
<![CDATA[ ## Remediation Suggestions - Pin a reviewed ChromaDB version and all relevant transitive dependencies in a lock file. - Use hash verification, such as `pip install --require-hashes -r requirements.txt`. - Install from an explicitly approved package index over TLS. - Perform installation in an isolated virtual environment with minimal privileges. - Add dependency vulnerability scanning and a controlled update process. - Document the supported Python and ChromaDB versions. Example requirement: ```text chromadb==<reviewed-version> --hash=sha256:<verified-package-hash> ``` ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • System Prompt LeakageDirect Leakage, Indirect Extraction, Tool-Based Exfiltration
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (12)

Direct Prompt Extraction

High
Category
System Prompt Leakage
Content
for rule_id, meta in zip(results["ids"][0], results["metadatas"][0]):
        path = kb / meta["file_path"]
        rules.append(json.loads(path.read_text()))
    return rules
```

### Load a problem schema template
Confidence
85% confidence
Finding
Skill contains instructions that could directly expose system prompts, internal rules, or hidden instructions to users or external parties.

Vague Triggers

Medium
Confidence
94% confidence
Finding
The description says to use the skill for many broad situations, including when the agent 'needs to understand what knowledge is available before making scheduling decisions' or 'wants to persist something learned in a conversation.' Those conditions are not narrowly bounded to specific trigger phrases or exclusion cases, which could cause the skill to be invoked for common scheduling tasks whenever knowledge might be relevant.

Ssd 3

Medium
Confidence
91% confidence
Finding
The skill encourages persisting conversation-derived customer information into a long-term filesystem knowledge base by default. That creates a data retention risk because sensitive operational details, personal data, or confidential client instructions may be stored indefinitely in plain files, indexed for retrieval, and committed to Git, expanding exposure and making deletion difficult.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
L140-L141 states that the agent 'NEVER writes directly to the main knowledge directories' and that all new knowledge goes to pending_review first. However, the confirmation flow for client memory updates in L268-L278 modifies `client_memory/_profile.json` directly in the live knowledge base, which contradicts that absolute claim about write behavior.

Ssd 3

Medium
Confidence
97% confidence
Finding
The proposal flow stores raw source quotes and session identifiers in persistent JSON files. If users share credentials, personal data, commercial terms, or sensitive shop-floor information in conversation, this design preserves that material verbatim and links it to a session, increasing disclosure risk through later reads, backups, indexing, or Git history.

Natural-Language Policy Violations

Medium
Confidence
98% confidence
Finding
The skill instructs the agent to present the proposal confirmation prompt using fixed Chinese text, but it does not offer the user a language or locale choice. This creates a language policy issue because the skill forces a specific language regardless of user preference or prior opt-in.

Ssd 3

Medium
Confidence
94% confidence
Finding
Persistent logging of scheduling decisions and session history can accumulate sensitive operational intelligence, user-provided content, and rule-usage patterns over time. In a local filesystem plus Git-backed design, those records may be broadly readable to local users, retained longer than intended, and difficult to purge once committed.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
This markdown file documents a script that deletes the existing `domain_rules` collection and rebuilds it from scratch, which can remove the current index state. Although the embedded code has a brief comment about a clean rebuild, the user-facing markdown description does not clearly warn that running the script performs a destructive reset.

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This section documents a helper that increments `use_count`, updates `last_used_at`, writes the rule JSON file back to disk, and may update ChromaDB metadata. The markdown description says it is called after each scheduling session, but it does not clearly warn users that persistent rule records are automatically modified.

Intent-Code Divergence

Low
Confidence
77% confidence
Finding
The heading at L357 describes deprecation as a soft disable that should prevent normal use, and the comment at L369 says the rule is removed from the vector index so it won't be retrieved. But the code at L372 updates the indexed metadata to `status: deprecated` rather than deleting the entry, so the implementation does not match the comment's stated action.

Natural-Language Policy Violations

Low
Confidence
88% confidence
Finding
This markdown file includes a natural-language schema example that sets `"language": "zh-CN"` as the client preference. Because the file does not indicate that language is selectable, optional, or region-specific, it can be read as enforcing a specific locale without user opt-in.

Missing User Warnings

Low
Confidence
84% confidence
Finding
The markdown instructs users to paste an inline helper that writes `_index.json`, but the surrounding description does not explicitly disclose that it modifies on-disk index metadata. For markdown-scoped warning checks, user-facing documentation should mention file modification when behavior affects stored data.

Static analysis

No suspicious patterns detected.