Back to skill

Security audit

phd-research-companion

Security checks for vulnerabilities and agentic risk

Overview

This research helper is not an obvious backdoor, but it is unsafe to install as-is because it scans beyond user-selected folders, encourages persistent jobs, and ships misleading or broken analysis tools.

Review before installing. Use only in a disposable or tightly scoped workspace, do not run the test suite as a safety check, do not enable the cron or nohup examples unless you understand how to stop them, and do not rely on its generated research analyses as factual until the placeholder/mock behavior is fixed.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • System PersistenceInstalls backdoors, hooks, services, or scheduled tasks that survive the run
  • 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
Findings (4)

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/paper_analyzer.py:365
Finding
Unrequested Discovery and Processing of PDFs Outside the Selected Input Scope<![CDATA[ ## Vulnerability Details **File Location**: `scripts/paper_analyzer.py`, lines 365-378 **Vulnerability Type**: Violation of least-privilege filesystem access **Risk Level**: Medium ### Complete Code Snippet ```python # Alternative: Check known paths alt_search_dirs = [ "/home/user/workspace/pdf", "/home/user/workspace/网站指纹", "/home/user/workspace" ] for alt_dir in alt_search_dirs: dir_path = Path(alt_dir) if dir_path.exists(): pdfs_here = list(dir_path.glob("*.pdf")) actual_pdfs.extend(pdfs_here]) actual_pdfs = list(set(actual_pdfs)) # Deduplicate ``` The closing bracket in `actual_pdfs.extend(pdfs_here])` above is represented as found in the audited context; the operative behavior is the extension of `actual_pdfs` with PDFs discovered in fixed workspace locations. ### Technical Analysis When the requested input directory contains no PDFs or is invalid, the analyzer does not stop at the user-authorized path. Instead, it enumerates PDFs in fixed directories, including `/home/user/workspace`, without explicit consent. This exceeds the minimum filesystem scope required for the declared task. The analyzer subsequently derives metadata from discovered filenames and generates analysis files based on that information. Although the implementation does not parse PDF contents, it can expose confidential paper titles, filenames, years, and local file associations in generated output. There is no evidence that this information is transmitted externally. The risk is unauthorized local discovery and processing rather than data exfiltration. ### Attack Path 1. A user invokes `paper_analyzer.py` with an empty, incorrect, or PDF-free input directory. 2. The requested search returns no usable PDFs. 3. The script automatically searches fixed workspace directories outside the requested path. 4. Unrelated PDFs are added to `actual_pdfs`. 5. Their filenames and inferred metadata are incorporated into generated analysis reports ...[truncated 712 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Remove all hard-coded fallback directories. 2. Restrict discovery to the path explicitly supplied through `--input-dir`. 3. If the input path does not exist, exit with a nonzero status and a clear error. 4. If no PDFs are found, report that condition and require the user to provide another path. 5. If multi-directory scanning is required, add an explicit repeatable option such as `--additional-input-dir`. 6. Display all directories that will be scanned and obtain confirmation before accessing paths not covered by the original input. 7. Resolve and validate each authorized path before traversal. 8. Add tests confirming that an empty input directory never causes access to sibling or workspace-level directories. A safer pattern is: ```python search_path = Path(args.input_dir).expanduser().resolve() if not search_path.exists(): parser.error(f"Input path does not exist: {search_path}") if search_path.is_file(): actual_pdfs = [search_path] if search_path.suffix.lower() == ".pdf" else [] else: actual_pdfs = list(search_path.glob("*.pdf")) if not actual_pdfs: parser.error(f"No PDF files found in authorized path: {search_path}") ``` ]]>

T09 · Insecure Skill Coding Practices

Error
Location
test_suit.py:25
Finding
Arbitrary Code Execution Through Dynamic Source Evaluation in the Test Suite<![CDATA[ ## Vulnerability Details **File Location**: `test_suit.py`, lines 25-29 **Vulnerability Type**: Unsafe dynamic code execution **Risk Level**: High ### Complete Code Snippet ```python # Try importing module to check syntax try: exec(open(full_path).read().split('if __name__')[0][:1000]) # Basic syntax check print(f"✅ Script exists: {script_path}") return True except Exception as e: print(f"⚠️ Syntax issue in {script_path}: {str(e)[:50]}") return False ``` ### Technical Analysis The function claims to perform a syntax check, but `exec()` evaluates source code in the current Python process. Any top-level statement placed in the first 1,000 characters of an expected script can execute. Neither truncating the source nor splitting it at `if __name__` creates a security boundary. Malicious code can be placed before that marker, and legitimate module initialization code can also produce unintended side effects. The paths are assembled from a fixed list of expected project scripts, which limits direct command-line path injection. Exploitation therefore requires one of those files, or the package containing them, to have been modified or replaced before the test suite is run. This is a realistic local package, archive, or repository supply-chain scenario. ### Attack Path 1. An attacker modifies or replaces one of the expected Python scripts in the project. 2. The attacker places a payload in the first 1,000 characters and before any `if __name__` marker. 3. A user runs `python3 test_suit.py`, believing it only validates installation and syntax. 4. `check_script_exists()` reads the modified source. 5. `exec()` executes the payload inside the test-suite process. 6. The payload inherits the invoking user's environment, current permissions, and accessible files. For example, a compromised script could contain an early top-level call that reads files, modifies project state, or launches another process. The audit found no such payload in ...[truncated 638 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Replace dynamic execution with parsing or compilation that does not run the source. Recommended implementation: ```python import ast try: source = full_path.read_text(encoding="utf-8") ast.parse(source, filename=str(full_path)) print(f"✅ Script syntax valid: {script_path}") return True except (OSError, SyntaxError) as e: print(f"⚠️ Syntax issue in {script_path}: {str(e)[:80]}") return False ``` Alternatively, use: ```python import py_compile py_compile.compile( str(full_path), doraise=True, ) ``` Additional hardening measures: 1. Never use `exec()` or `eval()` for installation or syntax validation. 2. Open source files with an explicit encoding and a context manager. 3. Separate syntax checks from behavioral tests. 4. Run behavioral tests in an isolated temporary environment with minimal permissions. 5. Verify package integrity or trusted checksums before testing downloaded archives. 6. Add a regression test containing a top-level side effect and verify that syntax validation does not execute it. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
init_research_project.py:35
Finding
Stored HTML Injection Through Unescaped Project Metadata<![CDATA[ ## Vulnerability Details **File Location**: `init_research_project.py`, lines 35-82 **Vulnerability Type**: Stored HTML and script injection **Risk Level**: Medium ### Complete Code Snippet ```python def create_dashboard_index(project_name: str, domain: str, journal: str, output_dir: Path): """Generate project overview HTML dashboard.""" html_content = f"""<!doctype html> <html lang="en-US"><head><meta charset="UTF-8"><title>{project_name} - Dashboard</title> <style>body{{font-family:Arial,sans-serif;padding:40px;background:#f5f5f5}} .container{{max-width:1200px;margin:0 auto;background:#fff;padding:30px;border-radius:8px;box-shadow:0 2px 4px rgba(0,0,0,0.1)}} h1{{color:#2c3e50}} .stats{{display:flex;gap:20px;margin:20px 0}} .stat-card{{background:#3498db;color:white;padding:20px;border-radius:6px;flex:1;text-align:center}} .stat-number{{font-size:2.5em;font-weight:bold}}table{{width:100%;border-collapse:collapse;margin:20px 0}} th,td{{padding:12px;text-align:left;border-bottom:1px solid #ddd}} th{{background:#ecf0f1}}</style></head> <body><div class="container"> <h1>🎓 Research Project Dashboard</h1> <p><strong>Domain:</strong> {domain}</p> <p><strong>Target Journal:</strong> {journal}</p> <p><strong>Created:</strong> {datetime.now().strftime('%Y-%m-%d %H:%M')}</p> ... </div></body></html> """ save_path = output_dir / "index.html" with open(save_path, 'w', encoding='utf-8') as f: f.write(html_content) ``` ### Technical Analysis The values `project_name`, `domain`, and `journal` originate from command-line arguments and are inserted directly into HTML markup. No HTML escaping or validation is performed. An attacker-controlled value can terminate the surrounding HTML context and inject arbitrary elements, including JavaScript. Because the payload is saved to `index.html`, this is a stored injection issue. It becomes active when the generated dashboard is opened in a browser. A representative malicious value would be ...[truncated 1516 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Escape every user-controlled value before inserting it into HTML: ```python from html import escape safe_project_name = escape(project_name, quote=True) safe_domain = escape(domain, quote=True) safe_journal = escape(journal, quote=True) ``` Use only the escaped values in the template: ```python <title>{safe_project_name} - Dashboard</title> <p><strong>Domain:</strong> {safe_domain}</p> <p><strong>Target Journal:</strong> {safe_journal}</p> ``` Further hardening: 1. Use an HTML template engine with automatic escaping enabled. 2. Add a restrictive Content Security Policy, for example: ```html <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src 'self' data:"> ``` 3. Avoid inline JavaScript entirely. 4. Validate expected metadata length and reject control characters. 5. Add tests using `<`, `>`, `"`, `'`, and script-like input. 6. Ensure generated links, attributes, and text nodes are escaped according to their specific HTML context. ]]>

T06 · System Persistence

Note
Location
SKILL.md:342
Finding
Optional Cron Configuration Creates Cross-Session Persistent Execution<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 342-353 **Additional Locations**: `BACKGROUND-RUN.md`, lines 123-140; `README.md`, lines 72-76 **Vulnerability Type**: Persistent scheduled-task configuration **Risk Level**: Low ### Complete Code Snippet ```bash ### Daily Literature Watch Updates Set up cron job for continuous domain monitoring: ```bash cd /home/user/workspace/skills/phd-research-companion/scripts crontab -e # Add daily at 8 AM (local time) 0 8 * * * python multi_source_search.py \ -q "your research topic" \ -l 5 \ --sources arxiv \ > /dev/null ``` ``` A more explicit variant also appears in `BACKGROUND-RUN.md`: ```bash # Edit crontab crontab -e # Add daily morning run at 8:00 AM 0 8 * * * cd /home/user/workspace/skills/phd-research-companion/scripts && \ python multi_source_search.py -q "machine unlearning" -l 5 -s arxiv > /tmp/literature-update.log 2>&1 ``` ### Technical Analysis The documentation instructs users to create a cron entry that survives the current terminal and Skill execution. This is a persistence mechanism because the configured command executes repeatedly in future sessions. The behavior is transparent, manually initiated, and directly related to the declared daily literature-monitoring feature. The audited scripts do not silently modify crontab. Consequently, this is not a concealed backdoor. Nevertheless, persistent scheduling is not required for ordinary one-shot project initialization, report generation, or paper analysis. It expands the duration and attack surface of the Skill. The scheduled command also relies on a mutable script path: if that script is later replaced, the replacement will execute automatically. Redirecting output to `/dev/null` can conceal failures or unexpected behavior. The documented command is also functionally inconsistent with the current script, which requires `--output-dir`. As written, the scheduled task is therefore likely to fail while r ...[truncated 1263 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Clearly label cron setup as optional and advanced. 2. Do not encourage scheduling until the underlying command has been verified interactively. 3. Supply all required arguments, including a dedicated absolute output directory. 4. Use absolute paths for both the Python interpreter and script. 5. Store the scheduled script in a directory writable only by the owning user. 6. Log output and errors to a protected, size-managed log rather than `/dev/null`. 7. Document how to inspect and remove the entry: ```bash crontab -l crontab -e ``` 8. Recommend a comment marker so users can identify the entry: ```cron # phd-research-companion: daily literature update 0 8 * * * /usr/bin/python3 /absolute/path/multi_source_search.py \ -q "your research topic" -l 5 --sources arxiv \ --output-dir /absolute/path/to/daily-results \ >> /absolute/path/to/logs/literature-update.log 2>&1 ``` 9. Prefer a scheduler configuration with explicit enable, disable, timeout, and failure-notification controls. 10. Pin or integrity-check the scheduled code if it may be updated automatically. 11. State that uninstalling or deleting the Skill does not automatically remove an existing cron entry. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Rogue AgentSelf-Modification, Session Persistence
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
Findings (46)

Ae1

High
Category
analysis-evasion
Content
### 4️⃣ LaTeX Template Generation (`scripts/generate_latex_template.py`)
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Tool Parameter Abuse

High
Category
Tool Misuse
Content
echo "=========================================="

# Cleanup temp if needed  
rm -f /tmp/phd-test-*.txt 2>/dev/null || true
Confidence
95% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Intent-Code Divergence

High
Confidence
98% confidence
Finding
The script advertises deep PDF extraction and analysis, but the implementation only parses filenames and emits hardcoded placeholders and fabricated-looking outputs. This is dangerous because users may rely on false analysis results for research, security, or decision-making, creating integrity and trust risks even without direct code execution.

exec() call detected

High
Category
Dangerous Code Execution
Content
# Try importing module to check syntax
    try:
        exec(open(full_path).read().split('if __name__')[0][:1000])  # Basic syntax check
        print(f"✅ Script exists: {script_path}")
        return True
    except Exception as e:
Confidence
99% confidence
Finding
The test helper reads Python source from a target script and passes it directly to exec(), which executes arbitrary code rather than performing a syntax-only validation. Because the path is built from repository-controlled script names, any malicious or compromised script in the skill can run code as soon as the test suite is executed, making this especially dangerous in an agent skill context where repository contents must be treated as untrusted.

Direct flow: open (file read) → exec (code execution)

High
Category
Data Flow
Content
# Try importing module to check syntax
    try:
        exec(open(full_path).read().split('if __name__')[0][:1000])  # Basic syntax check
        print(f"✅ Script exists: {script_path}")
        return True
    except Exception as e:
Confidence
99% confidence
Finding
This is a direct untrusted-code execution flow: file contents are read from disk and immediately executed with exec(). The comment says it is a basic syntax check, but in reality it runs script code, enabling arbitrary command execution, data access, or persistence if any checked script is malicious.

Session Persistence

Medium
Category
Rogue Agent
Content
# OpenClaw automatically manages background process IDs
```

### Method 2: Unix-style background with nohup
```bash
cd /home/user/workspace/skills/phd-research-companion/scripts
Confidence
65% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
# OpenClaw automatically manages background process IDs
```

### Method 2: Unix-style background with nohup
```bash
cd /home/user/workspace/skills/phd-research-companion/scripts
Confidence
65% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
```bash
cd /home/user/workspace/skills/phd-research-companion/scripts

nohup python multi_source_search.py \
    -q "research topic" \
    -s arxiv,semanticscholar \
    --limit 50 \
Confidence
89% confidence
Finding
The `nohup ... &` example explicitly launches a detached background process that survives terminal/session closure, which is a form of session persistence. In this research-tool context it is presented as convenience for long-running jobs, but detached processes reduce operator visibility and can continue network/file activity after the initiating interaction ends.

Session Persistence

Medium
Category
Rogue Agent
Content
```bash
# Edit crontab
crontab -e

# Add daily morning run at 8:00 AM
0 8 * * * cd /home/user/workspace/skills/phd-research-companion/scripts && \
Confidence
85% confidence
Finding
The document explicitly instructs users to set up a cron job, which establishes recurring execution outside the immediate session and therefore creates persistence. In skill context this appears intended for legitimate research automation, but persistent scheduled execution can continue consuming resources, operate on stale credentials or data, and be repurposed for unauthorized long-term activity if copied into a broader agent workflow.

Session Persistence

Medium
Category
Rogue Agent
Content
python generate_latex_template.py ...

# Only background the slow ones:
nohup python multi_source_search.py ... &
watch -n 10 'ps aux | grep multi_source_search'
```
Confidence
87% confidence
Finding
This recommendation tells users to background a script with `nohup ... &`, again creating a persistent detached process. Although not overtly malicious, normalizing persistence in quick-start guidance increases the chance that users run long-lived jobs without adequate supervision or cleanup.

Session Persistence

Medium
Category
Rogue Agent
Content
echo "Starting PhD research workflow automation..."

# Step 1: Literature (background)  
nohup python multi_source_search.py -q "$TOPIC" -l 20 > step1.log 2>&1 &
LITERATURE_PID=$!

# Wait and monitor
Confidence
90% confidence
Finding
The automation wrapper uses `nohup` to start a background task and then monitors the PID, which is a concrete persistent-execution pattern. Even in a legitimate pipeline, this can leave orphaned or hidden processes if monitoring fails, and it provides a ready-made pattern that could be adapted for stealthy long-running activity.

Session Persistence

Medium
Category
Rogue Agent
Content
### Cron Job for Daily Updates  
```bash
# Edit crontab: crontab -e
# Add: 0 8 * * * /path/to/run search --background -q "your_topic" > /dev/null
```
Confidence
85% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Medium
Confidence
92% confidence
Finding
The skill instructs users to initialize a project and create a substantial directory structure and related files, but it does not clearly warn that these commands will write to disk and modify the workspace. In an agent setting, this can lead to unexpected file creation or clutter, especially if the output path is mis-specified or the user assumes the action is read-only.

Session Persistence

Medium
Category
Rogue Agent
Content
#### Background Monitoring
```bash
# Terminal 1: Start background task  
./run search -q "topic" --background -o ./results &

# Terminal 2: Monitor progress (real-time)
Confidence
70% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Missing User Warnings

Medium
Confidence
97% confidence
Finding
The cron guidance sets up recurring automated execution of a networked search tool, causing ongoing background network activity and repeated file generation without a strong warning about persistence or resource usage. In an agent-assisted environment, users may not realize they are creating a durable scheduled task that continues after the current session ends.

Session Persistence

Medium
Category
Rogue Agent
Content
```bash
cd /home/user/workspace/skills/phd-research-companion/scripts
crontab -e

# Add daily at 8 AM (local time)
0 8 * * * python multi_source_search.py \
Confidence
98% confidence
Finding
The crontab instruction establishes persistence by creating a scheduled task that survives the current shell or agent session. Persistence is security-relevant because it can continue generating network traffic and files indefinitely, and users may forget it remains active.

Sudo/Root Execution

Medium
Category
Privilege Escalation
Content
### LaTeX Compilation Errors
```bash
# Common fix: Install missing packages or update template macros
sudo apt-get install texlive-latex-recommended texlive-science

# Verify template syntax
pdflatex --interaction=nonstopmode 03-paper-drafting/paper.tex 2>&1 | less
Confidence
96% confidence
Finding
The skill explicitly suggests running sudo apt-get install, which performs privileged system modification. Even though presented as troubleshooting, encouraging elevated execution in a skill increases risk if an agent or user follows it without understanding the consequences or verifying package trust and necessity.

External Transmission

Medium
Category
Data Exfiltration
Content
### Update Check
```bash
# Check for newer versions online
curl -s https://api.github.com/repos/openclaw/phd-research-companion/releases/latest | jq '.tag_name'

# Compare local version
grep "Version:" run
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Intent-Code Divergence

Medium
Confidence
97% confidence
Finding
The module docstring and argument help describe a general 'PhD research project initializer', but the generated dashboard always presents network traffic classification, evasion detection, and packet-analysis topics as the project's focus areas. This directly contradicts the apparent intent of initializing arbitrary research domains from user input.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The script documentation and CLI imply it initializes a project for an arbitrary research domain supplied via --domain, but the generated README states the research domain is 'Network Traffic Fingerprint Analysis' and fixes the venue text to IEEE TIFS wording. This is an active contradiction in generated documentation, not merely missing detail, because the output claims a specific project intent regardless of the actual input.

Intent-Code Divergence

Medium
Confidence
96% confidence
Finding
The top-level docstring says the tool checks coverage, completeness, mathematical consistency, revision documentation, and LaTeX formatting adherence. In code, only `run_literature_check` and `run_experiment_check` are implemented, yet the generated report includes many other checklist items as if they were part of the audit. This is an active contradiction between documentation and actual behavior, not merely an omitted detail.

Intent-Code Divergence

Medium
Confidence
90% confidence
Finding
`ComplianceChecker` is documented as a validator against submission standards, and `DEFAULT_CHECKLIST` enumerates multiple standards. However, only the literature and experiment-related methods actually set results; the remaining items are left unevaluated and later treated as failed in output and reporting. This diverges from the stated intent of validating those standards.

Intent-Code Divergence

Medium
Confidence
95% confidence
Finding
The module docstring states that the script downloads papers from arXiv, Semantic Scholar, and DBLP. In the actual implementation, only arXiv and Semantic Scholar branches exist in the source-processing loop, and there is no DBLP search function or branch, so the documentation materially overstates the implemented behavior.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The docstring says the function searches the arXiv API, which implies real retrieval behavior. The body explicitly contains a TODO for the actual request and returns fabricated mock results, so the documentation contradicts the function's true behavior.

Intent-Code Divergence

Medium
Confidence
98% confidence
Finding
The function is documented as performing a Semantic Scholar API search, but the body contains no network call and instead returns hardcoded mock records. This is a direct contradiction between the documented intent and the code's actual behavior.

Static analysis

No suspicious patterns detected.