Back to skill

Security audit

biomedical-reference-verifier

Security checks for vulnerabilities and agentic risk

Overview

This skill is a disclosed biomedical reference checker that writes local reports and uses public bibliographic lookups for its stated purpose.

Install only if you are comfortable with verification runs contacting Crossref, PubMed/NCBI, and OpenAlex with public identifiers and sometimes citation titles. For confidential drafts or unpublished research topics, use the documented format-only mode or disable providers, and provide contact email or API keys only when you intentionally want those sent to the providers.

Vulnerability Patterns
  • 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
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • Output HandlingUnvalidated Output Injection, Cross-Context Output, Unbounded Output
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (42)

Tp4

High
Category
MCP Tool Poisoning
Confidence
96% confidence
Finding

The description promises a user-facing skill for verifying authenticity of biomedical/life-science references, performing iterative checking and correction, and formatting citations into standard styles. This code chunk does not perform those tasks directly. It only parses an input JSON object, converts it between two internal representations via imported helper functions, writes the converted JSON, and reports warnings. While such conversion could be a supporting utility within a larger reference-auditing system, the chunk itself primarily serves artifact transformation rather than verification, normalization, or citation-style formatting. Therefore the supplied code does not accurately represent the declared primary purpose.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared purpose is a domain-specific citation/reference verification and formatting tool for biomedical literature. The supplied code chunk does not implement any reference parsing, identifier validation, bibliography normalization, evidence preservation, or report generation. Instead, it provides low-level infrastructure for managing network requests and retries. While such utilities could support a verifier system internally, this chunk's actual behavior is materially different from the declared end-user functionality and exposes undeclared network-oriented capabilities. Therefore this code chunk does not accurately represent the stated skill behavior on its own.

Content

No source excerpt is available for this finding.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 113)May include surrounding context.

md
- `reference-audit-report.html`: self-contained interactive report generated from the fixed template in `assets/reference-audit-report-template.html`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 185)May include surrounding context.

md
- `reference-audit-report.html`: self-contained interactive report generated from the fixed template in `assets/reference-audit-report-template.html`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

Scanner excerpt · SKILL.md (reported line 185)May include surrounding context.

md
h report layout and filtering | `assets/reference-audit-report-template.html`, `assets/reference-audit-report-template.en.html` | `scripts/test_privacy_language

Unvalidated Output Injection

High
Category
Output Handling
Confidence
95% confidence
Finding

Model output is used without validation or sanitization. Unvalidated output injected into downstream contexts (SQL, shell, HTML) enables injection attacks and arbitrary code execution.

Content

Scanner excerpt · scripts/self_test_verify_references.py (reported line 260)May include surrounding context.

python
],
        }
        audit.write_text(json.dumps(payload), encoding="utf-8")
        generated = subprocess.run(
            [sys.executable, str(HTML_GENERATOR), str(audit), "--output", str(output)],
            text=True,
            capture_output=True,

Unvalidated Output Injection

High
Category
Output Handling
Confidence
95% confidence
Finding

Model output is used without validation or sanitization. Unvalidated output injected into downstream contexts (SQL, shell, HTML) enables injection attacks and arbitrary code execution.

Content

Scanner excerpt · scripts/test_release_boundaries.py (reported line 19)May include surrounding context.

python
self.entry = v.ReferenceEntry(1, 'Smith JA. DNA methylation in human embryos. Journal A. 2024.', 'ama', 'DNA methylation in human embryos', ['Smith JA'], 'Journal A', '2024', '', '', '', '')

    def run_cli(self, source, output, *args):
        return subprocess.run([sys.executable, str(Path(v.__file__)), str(source), '--input-mode', 'records', '--pipeline', 'format-only', '--output-dir', str(output), *args], capture_output=True, text=True)

    def write_source(self, path, entries=None):
        text = json.dumps(v.render_normalized_records(entries or [self.entry]))

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
80% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · README.md (reported line 18)May include surrounding context.

md
- Tolerant handling of user-edited prior artifacts
- Runtime metrics and request-event logging
- No hidden persistent cache
- Severe references are reported, never automatically deleted

## Requirements

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
89% confidence
Finding

The skill advertises and orchestrates capabilities that include local Python execution, filesystem read/write, environment variable access, shell invocation, and network access, but it does not declare an explicit tool permission scope. That creates an avoidable trust gap: a host may grant broader capabilities than reviewers or users expect, increasing the risk of over-privileged execution or accidental data exposure during reference verification workflows.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
50% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/verification_policy.md (reported line 49)May include surrounding context.

md
- Use DOI verification first: `GET https://api.crossref.org/works/{doi}`. This is the primary source of DOI/title/author/journal/year truth.
- Start enabled evidence lines together. Fast stops after primary evidence, Balanced allows a 2.5-second auxiliary grace period after primary DOI lookup and enabled supplied-PMID lookup, and Strict waits for every enabled line to complete or explicitly fail. Network timeout, rate limiting, not-found, parsing failure, mode skip, and expired budget must remain distinguishable.
- If PubMed and OpenAlex are disabled with `--pubmed-mode off --openalex-mode off`, do not apply the grace timer to Crossref; Crossref DOI verification must complete because it is the primary authority.
- OpenAlex checks use `GET https://api.openalex.org/works/doi:{doi}` or external IDs such as `pmid:{pmid}` for PMID, and `GET https://api.openalex.org/works?search={title}&per_page=3` for DOI-missing title recovery.
- If an entry has no DOI, use title recovery in this order: Crossref title query first, OpenAlex title query only if the Crossref match is weak, PubMed title query only if biomedical corroboration is still needed. Record recovered DOI values in the report and ask before adding them to formatted citations.
- Run quality-checked DOI-missing title recovery before full identifier verification, deduplicated and with provider-specific bounded concurrency. A recovered DOI may drive verification but must not silently modify the user source.
- Do not use title search to silently override an existing DOI result. Existing DOI conflicts should be reported as conflicts, hijacking, or shifted identifiers.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · references/verification_policy.md (reported line 69)May include surrounding context.

md
## Network risk policy

- Default DOI/PMID verification is low risk and should run without asking the user each time. The payload is limited to public identifiers required for the skill's purpose.
- Default requests must not include `original_text`, manuscript paragraphs, abstracts, local evidence notes, or unpublished claims.
- DOI-missing recovery may send only a short, quality-checked `source.title`.
- Any deep search that sends title plus abstract, surrounding context, or manuscript-derived claims is higher risk and must be separated from default batch verification.

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/self_test_verify_references.py (reported line 57)May include surrounding context.

python
str(output_dir),
        *extra,
    ]
    return subprocess.run(cmd, text=True, capture_output=True, check=False)


def assert_ok(result: subprocess.CompletedProcess[str]) -> None:

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/self_test_verify_references.py (reported line 172)May include surrounding context.

python
initial = run_verifier(records, audit_dir, "--keep-process-json")
        assert_ok(initial)
        audit = audit_dir / "reference-audit.json"
        to_index = subprocess.run([sys.executable, str(CONVERTER), str(audit), "--to", "index", "--output", str(index)], text=True, capture_output=True)
        assert_ok(to_index)
        to_audit = subprocess.run([sys.executable, str(CONVERTER), str(index), "--to", "audit", "--output", str(round_trip)], text=True, capture_output=True)
        assert_ok(to_audit)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/self_test_verify_references.py (reported line 174)May include surrounding context.

python
audit = audit_dir / "reference-audit.json"
        to_index = subprocess.run([sys.executable, str(CONVERTER), str(audit), "--to", "index", "--output", str(index)], text=True, capture_output=True)
        assert_ok(to_index)
        to_audit = subprocess.run([sys.executable, str(CONVERTER), str(index), "--to", "audit", "--output", str(round_trip)], text=True, capture_output=True)
        assert_ok(to_audit)
        original_payload = json.loads(audit.read_text(encoding="utf-8"))
        round_payload = json.loads(round_trip.read_text(encoding="utf-8"))

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/self_test_verify_references.py (reported line 260)May include surrounding context.

python
],
        }
        audit.write_text(json.dumps(payload), encoding="utf-8")
        generated = subprocess.run(
            [sys.executable, str(HTML_GENERATOR), str(audit), "--output", str(output)],
            text=True,
            capture_output=True,

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · references/verification_policy.md (reported line 46)May include surrounding context.

md
for email in ('','contact@example.org'):
            with self.subTest(email=email),patch.object(v.urllib.request,'urlopen') as request:
                request.return_value.__enter__.return_value.read.return_value=b'{}'
                v.ApiClient(email=email).get_bytes('https://api.crossref.org/works')
                header=request.call_args.args[0].get_header('User-agent')
                self.assertEqual('mailto:' in header,bool(email))
                if email:self.assertIn(email,header)

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

Scanner excerpt · scripts/test_privacy_language.py (reported line 48)May include surrounding context.

python
for email in ('','contact@example.org'):
            with self.subTest(email=email),patch.object(v.urllib.request,'urlopen') as request:
                request.return_value.__enter__.return_value.read.return_value=b'{}'
                v.ApiClient(email=email).get_bytes('https://api.crossref.org/works')
                header=request.call_args.args[0].get_header('User-agent')
                self.assertEqual('mailto:' in header,bool(email))
                if email:self.assertIn(email,header)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/test_privacy_language.py (reported line 68)May include surrounding context.

python
with tempfile.TemporaryDirectory() as td:
            root=Path(td);source=root/'input.json'
            source.write_text(json.dumps({'records':[{'index':1,'source':{'original_text':'Smith JA. DNA methylation in human embryos. 2024.','title':'DNA methylation in human embryos','authors':['Smith JA'],'year':'2024'}}]}))
            result=subprocess.run([sys.executable,str(Path(v.__file__)),str(source),'--pipeline','format-only','--report-language','en','--output-dir',str(root/'out'),'--keep-process-json'],capture_output=True,text=True)
            self.assertEqual(result.returncode,0,result.stderr)
            self.assertIn('lang="en"',(root/'out/reference-audit-report.html').read_text())
            result=subprocess.run([sys.executable,str(Path(h.__file__)),str(root/'out/reference-audit.json'),'--language','en','--output',str(root/'standalone.html')],capture_output=True,text=True)

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/test_privacy_language.py (reported line 71)May include surrounding context.

python
result=subprocess.run([sys.executable,str(Path(v.__file__)),str(source),'--pipeline','format-only','--report-language','en','--output-dir',str(root/'out'),'--keep-process-json'],capture_output=True,text=True)
            self.assertEqual(result.returncode,0,result.stderr)
            self.assertIn('lang="en"',(root/'out/reference-audit-report.html').read_text())
            result=subprocess.run([sys.executable,str(Path(h.__file__)),str(root/'out/reference-audit.json'),'--language','en','--output',str(root/'standalone.html')],capture_output=True,text=True)
            self.assertEqual(result.returncode,0,result.stderr)
            self.assertIn('lang="en"',(root/'standalone.html').read_text())

subprocess module call

Medium
Category
Dangerous Code Execution
Confidence
70% confidence
Finding

subprocess module calls execute external commands. Without careful input validation, this enables command injection.

Content

Scanner excerpt · scripts/test_release_boundaries.py (reported line 19)May include surrounding context.

python
self.entry = v.ReferenceEntry(1, 'Smith JA. DNA methylation in human embryos. Journal A. 2024.', 'ama', 'DNA methylation in human embryos', ['Smith JA'], 'Journal A', '2024', '', '', '', '')

    def run_cli(self, source, output, *args):
        return subprocess.run([sys.executable, str(Path(v.__file__)), str(source), '--input-mode', 'records', '--pipeline', 'format-only', '--output-dir', str(output), *args], capture_output=True, text=True)

    def write_source(self, path, entries=None):
        text = json.dumps(v.render_normalized_records(entries or [self.entry]))

Natural-Language Policy Violations

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The test explicitly requires the generated reference text to contain the Chinese phrase "不可用". This encodes a language-specific output requirement in natural language without any indication of user choice or a documented region-specific constraint, which matches the locale policy violation category.

Content

No source excerpt is available for this finding.

Autonomous Decision Making

Medium
Category
Excessive Agency
Confidence
75% confidence
Finding

Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.

Content

Scanner excerpt · scripts/verifier_models.py (reported line 1)May include surrounding context.

python
"""Shared data models. This module contains no verification behavior."""

from __future__ import annotations

External Transmission

Medium
Category
Data Exfiltration
Confidence
97% confidence
Finding

This skill sends reference identifiers and optionally a user-supplied email address to Crossref over the network. In the skill context, bibliography/manuscript content can be sensitive or unpublished, so transmitting DOI/title-derived queries to third-party services can leak research topics, draft citations, and contact metadata outside the local environment.

Content

Scanner excerpt · scripts/verify_references.py (reported line 792)May include surrounding context.

python
return self.crossref_doi_cache[doi]
        try:
            params = urllib.parse.urlencode({"mailto": self.email} if self.email else {})
            url = "https://api.crossref.org/works/" + urllib.parse.quote(doi, safe="") + "?" + params
            item = self.get_json(url).get("message", {})
            record = crossref_record(item, score=1.0)
        except Exception:

External Transmission

Medium
Category
Data Exfiltration
Confidence
98% confidence
Finding

Crossref title search transmits raw query titles, which may directly reveal confidential manuscript content, research directions, or proprietary drug/biomedical topics. Because this tool is specifically designed for biomedical references, the external disclosure risk is more sensitive than in generic citation formatting due to possible prepublication and health-related subject matter.

Content

Scanner excerpt · scripts/verify_references.py (reported line 818)May include surrounding context.

python
"rows": str(limit),
                **({"mailto": self.email} if self.email else {}),
            }
            url = "https://api.crossref.org/works?" + urllib.parse.urlencode(params)
            items = self.get_json(url)["message"]["items"]
            records = []
            for item in items:

External Transmission

Medium
Category
Data Exfiltration
Confidence
97% confidence
Finding

OpenAlex DOI lookups disclose the identifiers being checked and may also include optional mailto/api_key metadata. While DOI-only queries are less sensitive than full title searches, they still reveal what works a user is validating and may expose manuscript composition patterns to an external provider.

Content

Scanner excerpt · scripts/verify_references.py (reported line 993)May include surrounding context.

python
"select": "id,doi,title,display_name,publication_year,authorships,primary_location,ids",
                }
            )
            url = f"https://api.openalex.org/works/{identifier}?" + urllib.parse.urlencode(params)
            record = openalex_record(self.get_json(url), score=1.0)
        except Exception:
            record = None

Static analysis

No suspicious patterns detected.