Back to skill

Security audit

narrative-topology

Security checks for vulnerabilities and agentic risk

Overview

This skill is a local document-graph scanner with some scope and reliability cautions, but I found no hidden persistence, credential access, network exfiltration, or destructive behavior.

Before installing, understand that running the scanner will recursively read supported text-like files under the directory where it is launched and print extracted node names and graph data locally. Run it from a small, intentional folder and avoid untrusted or very large documents; also note that predicates are not preserved in the adjacency matrix.

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

T09 · Insecure Skill Coding Practices

Warning
Location
scanner.py:12
Finding

Unbounded Graph Expansion and Quadratic Memory Allocation

Content
View full analysis

Vulnerability Details

File Location: scanner.py:12-17, 66-69, 109
Vulnerability Type: Uncontrolled resource consumption
Risk Level: Medium

Vulnerable Code

python
def list_files_recursive(directory, extensions=('.txt', '.def', '.erl', '.ex', '.md')):
    """Yield all files with given extensions under directory."""
    for root, _, files in os.walk(directory):
        for f in files:
            if f.endswith(extensions):
                yield os.path.join(root, f)
python
for subj in subjects:
    for obj in objects:
        edges.append((subj, obj))
python
matrix = [[0] * n for _ in range(n)]

Technical Analysis

The scanner recursively processes every supported file beneath the current working directory without enforcing limits on directory depth, file count, individual file size, total input size, unique node count, or generated edge count.

A triple containing parallel subject and object lists produces their Cartesian product. If there are S subjects and O objects, the parser creates S × O edge tuples. Once parsing is complete, every unique subject and object becomes a graph node, and the scanner allocates a dense n × n adjacency matrix. Consequently, matrix memory usage grows quadratically with the number of unique nodes, even when the graph itself is sparse.

An attacker-controlled or unexpectedly large supported document can therefore consume excessive CPU and memory. Recursive directory scanning can amplify this condition when many crafted files are placed anywhere below the execution directory.

Attack Path

  1. An attacker supplies a supported file, such as a Markdown document, or places it beneath a directory that the user will scan.
  2. The file contains triples with large parallel subject and object lists, many unique node names, or both.
  3. The scanner recursively discovers and parses the file.
  4. Cartesian-product processing creates a large number of in-memory edge tuples.
  5. The scanner c ...[truncated 877 chars]
Remediation
View remediation

Remediation Suggestions

  1. Enforce configurable upper bounds on:

    • Number of scanned files.
    • Directory traversal depth.
    • Individual and aggregate input size.
    • Subject and object list lengths.
    • Generated edges.
    • Unique graph nodes.
  2. Reject oversized input before Cartesian expansion and return a clear error rather than silently continuing.

  3. Replace the dense adjacency matrix with a sparse representation, such as a dictionary mapping each source node to a set of target nodes:

python
from collections import defaultdict

adjacency = defaultdict(set)
for source, target in edges:
    adjacency[source].add(target)
  1. If matrix output is mandatory, generate and print one row at a time rather than retaining the entire matrix in memory.

  2. Deduplicate edges during parsing to reduce unnecessary storage and ensure statistics reflect unique graph edges.

  3. Allow callers to specify an explicit input file or constrained input directory instead of always recursively scanning the current working directory.

  4. Document safe operating limits and consider operating-system resource restrictions when processing untrusted documents.

Vulnerability Patterns
  • 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
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
Findings (5)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
90% confidence
Finding

The skill includes and instructs use of Python code that recursively reads local files, but it declares no explicit tool scope or permissions boundary. In an agent environment, that mismatch can lead to unintended access to nearby markdown/text files and makes the skill's file-reading behavior non-transparent to users and policy systems.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
88% confidence
Finding

The documentation directs users to run a scanner that recursively walks the current working directory and reads all matching .txt, .def, .erl, .ex, and .md files without an explicit warning. In practice, this can unintentionally ingest unrelated local content, including sensitive notes, architecture docs, or transcripts, especially because the skill is framed as operating on dense discussions and markdown corpora.

Content

No source excerpt is available for this finding.

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The manifest and surrounding documentation repeatedly describe extracting semantic relationships using RDF-style triples, where the predicate is part of the relation. But the scanner explicitly ignores the predicate (p is ignored) and only emits (subject, object) edges, collapsing distinct relations like causes, blocks, or depends_on into the same adjacency structure. That is a meaningful behavior mismatch because the implemented output no longer preserves the RDF-style semantics it claims to analyze.

Content

No source excerpt is available for this finding.

Vague Triggers

Low
Category
Not specified by scanner
Confidence
88% confidence
Finding

The description says the skill works on long narratives, architectures, or complex discussions and extracts signal from dense multi-turn discussions, but it does not define specific invocation phrases, scope boundaries, or negative examples. In a markdown skill description, this broad wording can overlap with many ordinary analysis requests and make activation conditions ambiguous.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
83% confidence
Finding

The example output shows a row 1::14, implying every value in that row is 1, while the code constructs a sparse binary adjacency matrix by setting only observed edges to 1. With the documented Hamlet example containing 12 edges across 14 nodes, that illustrated row is inconsistent with what the code would actually emit and may mislead users about the scanner's behavior.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.