Back to skill

Security audit

Medication Reconciliation

Security checks for vulnerabilities and agentic risk

Overview

The skill has a legitimate medication-reconciliation purpose, but its executable can produce misleading clinical reports and exposes identifiable patient data too casually.

Review this skill carefully before installing in any real clinical or PHI-handling environment. Use only de-identified or authorized data, do not rely on the executable report for clinical decisions without manual pharmacist or physician review, avoid printing full reports in shared or logged terminals, and remove or pin the unnecessary requirements before installation.

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

T08 · Insecure Dependencies

Warning
Location
requirements.txt:1
Finding

Unnecessary and unpinned third-party packages create supply-chain exposure

Content
View full analysis

Vulnerability Details

File Location: requirements.txt:1-2
Vulnerability Type: Unnecessary and unpinned dependencies
Risk Level: Medium

Vulnerable Code

text
dataclasses
difflib

Technical Analysis

The project declares dataclasses and difflib as third-party dependencies without version pins or integrity hashes. Both modules are included in the Python standard library for the modern Python versions supported by the script's syntax and type annotations.

In particular, declaring difflib causes package installers to resolve a package from an external package index even though the application only requires the standard-library module. This unnecessarily expands the trusted computing base and creates dependency-confusion or package-compromise exposure. The unpinned dataclasses declaration similarly permits installation of any version returned by the configured package index.

Package installation can execute package build logic with the permissions of the user running pip. Therefore, compromise of one of these unnecessary packages, a configured package index, or the dependency-resolution path could lead to code execution during environment setup.

Attack Path

  1. An operator follows the conventional setup process and runs pip install -r requirements.txt.
  2. pip contacts its configured package index and resolves the unpinned dataclasses and difflib names.
  3. A compromised, substituted, or maliciously mirrored package is downloaded.
  4. Package build or installation logic executes with the operator's current privileges.
  5. The malicious package can then modify files, access data available to that account, or implant code into the Python environment.

Exploitation depends on compromise or manipulation of the package source or resolution environment; the audited repository itself does not contain such a payload.

Impact Assessment

Successful exploitation could obtain arbi ...[truncated 338 chars]

Remediation
View remediation

Remediation Suggestions

  1. Remove both declarations when the project targets a modern Python version:
    text
    # No external runtime dependencies
    
  2. Explicitly document the minimum supported Python version, preferably Python 3.8 or later.
  3. If legacy Python support genuinely requires the dataclasses backport, use an environment marker and an exact version:
    text
    dataclasses==0.8; python_version < "3.7"
    
  4. Generate and verify cryptographic hashes for every unavoidable external dependency.
  5. Install dependencies from a trusted, allowlisted package index and perform package vulnerability and provenance scanning in CI.
  6. Add a CI check that rejects standard-library module names in dependency manifests.

T09 · Insecure Skill Coding Practices

Error
Location
scripts/main.py:77
Finding

Missing patient-ID and dose-change validation permits misleading reconciliation reports

Content
View full analysis

Vulnerability Details

File Location: scripts/main.py:77-96, scripts/main.py:143-162, and scripts/main.py:172-195
Vulnerability Type: Missing input-integrity validation
Risk Level: High

Vulnerable Code

python
def load_pre_admission(self, filepath: str) -> None:
    """Load pre-admission medication list"""
    with open(filepath, 'r', encoding='utf-8') as f:
        data = json.load(f)

    self.patient_id = data.get('patient_id', 'Unknown')
    self.patient_name = data.get('patient_name', 'Unknown')
    self.admission_date = data.get(
        'admission_date',
        datetime.now().strftime('%Y-%m-%d')
    )

    self.pre_admission_meds = [
        Medication.from_dict(med) for med in data.get('medications', [])
    ]

def load_inpatient_orders(self, filepath: str) -> None:
    """Load inpatient medication orders"""
    with open(filepath, 'r', encoding='utf-8') as f:
        data = json.load(f)

    self.inpatient_meds = [
        Medication.from_dict(med) for med in data.get('medications', [])
    ]

The inpatient patient_id is neither retained nor compared with the pre-admission patient_id.

python
def _check_duplicate(
    self,
    pre_med: Medication,
    in_med: Medication
) -> bool:
    """Check for duplicate medication (same drug, different names)"""
    if not pre_med.generic_name or not in_med.generic_name:
        return False

    if self._calculate_similarity(
        pre_med.generic_name,
        in_med.generic_name
    ) >= 0.9:
        pre_dose = ''.join(
            filter(str.isdigit, pre_med.dosage)
        ) if pre_med.dosage else ""
        in_dose = ''.join(
            filter(str.isdigit, in_med.dosage)
        ) if in_med.dosage else ""
        if pre_dose and in_dose and pre_dose == in_dose:
            return True
    return False
python
if best_match and score >= self.f
...[truncated 3496 chars]
Remediation
View remediation

Remediation Suggestions

  1. Require a non-empty patient_id in both input documents.
  2. Store the inpatient identifier separately and reject mismatches before processing:
    python
    inpatient_patient_id = data.get("patient_id")
    if not inpatient_patient_id:
        raise ValueError("Inpatient patient_id is required")
    if inpatient_patient_id != self.patient_id:
        raise ValueError("Patient IDs do not match")
    
  3. Validate both documents against an explicit JSON Schema, including required medication fields and expected data types.
  4. Implement a dedicated dose_changed collection before classifying a name match as continued.
  5. Compare normalized dose quantity and unit using a tested medication-dose parser rather than stripping non-digit characters.
  6. Include frequency and route changes in the discrepancy report.
  7. Reject ambiguous or unparsable doses, or flag them for mandatory manual review rather than treating them as unchanged.
  8. Add regression tests covering:
    • Different patient IDs.
    • Missing patient IDs.
    • 500 mg versus 1000 mg.
    • Decimal quantities such as 0.5 mg.
    • Equivalent and non-equivalent unit conversions.
    • Frequency and route changes.
  9. Ensure generated reports explicitly record both source identifiers and the validation result for auditability.
  10. Correct the bundled audit result and documentation so they do not claim executable dose-change behavior until that behavior is implemented and tested.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (6)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill declares executable usage that reads input files and can write output files, but it does not explicitly constrain tool scope with permissions or allowed-tools metadata. In an agent environment, this can cause the runtime to grant broader filesystem access than intended, which is especially sensitive here because the skill processes medication data that may contain PHI.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The tool writes a reconciliation report containing patient identifiers, medication history, inpatient orders, and clinical warnings directly to an arbitrary output path with no redaction, access-control checks, or warning that the content is protected health information. In a healthcare context this creates a real confidentiality risk because the file may be stored in insecure locations, synced, backed up, or shared beyond authorized staff.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

Printing the full JSON reconciliation report to stdout exposes sensitive patient and medication data to terminal history, shell logging, process supervisors, CI logs, remote session capture, and shared consoles. Because this skill is explicitly designed for medication reconciliation, the data is highly sensitive clinical information, making casual console disclosure a meaningful privacy and compliance vulnerability.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The manifest describes a skill that compares medication lists to identify omissions and duplicates, which does not inherently require creating files on disk. The audit explicitly notes that the --example flag generates files as a side effect, introducing a capability outside the core reconciliation purpose.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
60% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · requirements.txt (reported line 1)May include surrounding context.

text
dataclasses
difflib

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
60% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · requirements.txt (reported line 2)May include surrounding context.

text
dataclasses
difflib

Static analysis

No suspicious patterns detected.