Back to skill

Security audit

Clinical Data Cleaner

Security checks for vulnerabilities and agentic risk

Overview

This skill appears intended for clinical data cleaning, but it has data-integrity and privacy-scope gaps that users should review before use with real trial data.

Review this skill carefully before installing for real clinical trial data. Use only approved, non-sensitive test data unless your environment has appropriate privacy controls, choose output paths that cannot overwrite raw data, preserve original datasets separately, and independently validate SDTM compliance before relying on generated outputs or reports.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/main.py:395
Finding

Input Clinical Data Can Be Overwritten Without Safeguards

Content
View full analysis

Vulnerability Details

File Location: scripts/main.py:395-410
Vulnerability Type: Unrestricted output overwrite and loss of source-data integrity
Risk Level: High

Vulnerable Code

python
# Save output
output_path = Path(args.output)
if output_path.suffix.lower() == '.csv':
    df_cleaned.to_csv(args.output, index=False)
elif output_path.suffix.lower() in ['.xlsx', '.xls']:
    df_cleaned.to_excel(args.output, index=False)
else:
    # Default to CSV
    df_cleaned.to_csv(args.output, index=False)

print(f"Cleaned data saved: {args.output}")

# Save report
cleaner.save_report(args.output)

Technical Analysis

The application accepts independently controlled --input and --output paths but does not resolve and compare them before writing the cleaned dataset. Pandas output methods overwrite an existing destination by default. Consequently, selecting the source file itself as the output replaces the original clinical dataset.

The implementation also does not require explicit authorization before replacing another existing output file and does not use an atomic temporary-file workflow. A failure during writing may therefore leave an incomplete or corrupted file.

This behavior conflicts with the project's own guidance in references/common-patterns.md, which requires saving to a new file and preserving raw data. In a clinical-data workflow, preservation of source records is necessary for reproducibility, provenance, independent validation, and regulatory auditability.

Attack Path

  1. An attacker, unsafe wrapper, or mistaken operator supplies a valid clinical dataset as --input.
  2. The same path is supplied for --output, for example:
    bash
    python scripts/main.py \
      --input clinical/raw_dm.csv \
      --domain DM \
      --output clinical/raw_dm.csv
    
  3. The application reads the original file into memory.
  4. After transformation, to_csv() or ...[truncated 1276 chars]
Remediation
View remediation

Remediation Suggestions

  1. Resolve and compare canonical input and output paths before processing:

    python
    input_path = Path(args.input).expanduser().resolve()
    output_path = Path(args.output).expanduser().resolve()
    
    if input_path == output_path:
        parser.error("--output must be different from --input")
    
  2. Refuse to replace an existing output by default:

    python
    if output_path.exists():
        parser.error(
            f"Output already exists: {output_path}. "
            "Choose a new path or use an explicit overwrite option."
        )
    
  3. If overwrite support is operationally necessary, require an explicit --overwrite option and continue to prohibit overwriting the input under all circumstances.

  4. Write to a temporary file in the destination directory, flush and synchronize it, validate that serialization completed successfully, and atomically rename it to the final destination.

  5. Apply equivalent collision checks to the generated .report.json path.

  6. Record canonical input and output paths, source-file hash, output hash, software version, and overwrite authorization in the audit report.

  7. Add automated tests covering identical paths, symlink aliases, relative-path aliases, pre-existing outputs, interrupted writes, and report-path collisions.

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/main.py:320
Finding

Processing Continues After Mandatory SDTM Validation Failure

Content
View full analysis

Vulnerability Details

File Location: scripts/main.py:320-323
Vulnerability Type: Fail-open validation of mandatory clinical-data fields
Risk Level: Medium

Vulnerable Code

python
# Validate domain
is_valid, missing_fields = self.validate_domain(df)
if not is_valid:
    print(f"\nWarning: Missing required fields: {missing_fields}")
    print("Continuing with available fields...")

Technical Analysis

validate_domain() identifies missing mandatory fields for the selected DM, LB, or VS domain. The cleaning pipeline nevertheless treats validation failure as a warning and proceeds with imputation, outlier handling, date conversion, output serialization, and report generation.

This is a fail-open design: the validation control detects invalid structure but does not enforce the required invariant. It also conflicts with SKILL.md, which states that processing should not continue when critical input is missing.

Mandatory identifiers and measurements are security-relevant data-integrity controls in this context. For example, missing USUBJID can prevent reliable attribution of observations to subjects, while missing test or result fields can make outlier analysis incomplete or misleading. Because the program still produces a file described as cleaned data, downstream automation may consume structurally invalid output.

Attack Path

  1. An attacker or compromised upstream data source prepares a CSV or Excel file that omits one or more mandatory fields, such as USUBJID, LBTESTCD, or LBDTC.
  2. The file is passed to the cleaner with the corresponding domain.
  3. validate_domain() detects and returns the missing-field list.
  4. The pipeline prints a warning but does not raise an exception or return a failure status.
  5. Remaining transformations are applied to the incomplete dataset.
  6. The invalid dataset and an audit report are written normally.
  7. A downstream user or automated pro ...[truncated 1042 chars]
Remediation
View remediation

Remediation Suggestions

  1. Fail closed by default when mandatory fields are absent:

    python
    is_valid, missing_fields = self.validate_domain(df)
    if not is_valid:
        raise ValueError(
            f"Missing required fields for {self.domain}: "
            f"{', '.join(sorted(missing_fields))}"
        )
    
  2. Return a nonzero CLI exit status and do not create either the cleaned output or the success report after validation failure.

  3. If partial processing is a genuine business requirement, require an explicit option such as --allow-incomplete-domain. Record the missing fields, authorization, rationale, and degraded validation status prominently in the report.

  4. Distinguish successful reports from failed or partial reports using a machine-readable status such as valid, rejected, or partial.

  5. Validate more than column presence, including field types, controlled terminology, subject identifier completeness, duplicate keys, required values, and domain-specific constraints.

  6. Ensure downstream systems check the machine-readable validation status rather than relying on process completion or the existence of an output file.

  7. Add tests confirming that every missing mandatory field causes failure by default and that no output artifact is written on validation failure.

Vulnerability Patterns
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • 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 (28)

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Packaged executable path(s): `scripts/main.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Packaged executable path(s): `scripts/main.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Packaged executable path(s): `scripts/main.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Packaged executable path(s): `scripts/main.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Packaged executable path(s): `scripts/main.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Packaged executable path(s): `scripts/main.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Packaged executable path(s): `scripts/main.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Packaged executable path(s): `scripts/main.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Packaged executable path(s): `scripts/main.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Packaged executable path(s): `scripts/main.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Packaged executable path(s): `scripts/main.py`.

Ae1

High
Category
analysis-evasion
Confidence
100% confidence
Finding

Referenced artifact was not completely inspected

Content

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

md
- Packaged executable path(s): `scripts/main.py`.

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill advertises executable workflows that read and write files, but it does not declare any explicit tool scope such as allowed-tools or permissions. In an agent setting, that ambiguity can cause the runtime to grant broader filesystem access than intended, increasing the chance of unauthorized reads, overwrites, or handling of regulated clinical data outside approved boundaries.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
92% confidence
Finding

The skill is designed to process and save clinical trial data, which commonly contains sensitive and regulated information, yet the documentation omits any warning or handling constraints for protected data. In practice, this can lead users or agents to move patient-related datasets through insecure paths, store outputs without safeguards, or violate privacy and regulatory controls.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The script persists cleaned clinical trial data to disk and also writes a JSON report containing details about processing actions, but it provides no explicit privacy warning, confirmation, or safeguards around handling regulated or potentially identifiable clinical data. In the context of FDA/EMA submission workflows, silent persistence increases the risk of accidental storage of PHI/PII or sensitive trial data on insecure endpoints, shared directories, or developer workstations.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
90% confidence
Finding

The manifest and surrounding documentation consistently describe this skill as operating on clinical trial datasets such as SDTM/CRF data files. However, the audit-ready example passes a free-text clinical narrative string to --input, which conflicts with the stated file-based data-cleaning workflow and could mislead users about what the skill actually handles.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

The dependency list uses an unpinned package name for numpy, allowing different versions to be installed over time or across environments. This weakens reproducibility and can unintentionally introduce vulnerable or breaking releases into a clinical-data processing workflow, especially problematic for regulated submissions and auditability.

Content

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

text
numpy
pandas
scipy

Unverifiable Dependency: numpy has 16 known advisory(ies) (CVE-2014-1859 (Numpy arbitrary file write via symlink attack); CVE-2021-41495 (NumPy NULL Pointer Dereference); CVE-2021-33430 (NumPy Buffer Overflow (Disputed)) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
89% confidence
Finding

Numpy has known advisories, but because no version is pinned, it is impossible to verify whether the deployed environment is affected. That uncertainty is itself a security weakness: a fresh install could select a vulnerable release, and the clinical/regulatory context increases concern because data pipelines should be deterministic and auditable.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

The pandas dependency is unpinned, so builds may resolve to different versions without review. In a clinical data cleaning skill, that creates supply-chain and reproducibility risk, and may pull in a version with known security or integrity issues affecting regulated data handling.

Content

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

text
numpy
pandas
scipy

Unverifiable Dependency: pandas has 1 known advisory(ies) (CVE-2020-13091 (** DISPUTED ** pandas through 1.0.3 can unserialize and execute commands from an)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
84% confidence
Finding

Pandas has at least one reported advisory, and the absence of version pinning means the project cannot demonstrate that installed versions are unaffected. In a tool handling clinical datasets, unverifiable dependency state undermines both security assurance and compliance expectations around controlled software environments.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

Scipy is declared without a version constraint, which permits uncontrolled dependency drift. This is dangerous because future installs may include vulnerable or incompatible releases, undermining both security posture and consistency of clinical data transformations.

Content

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

text
numpy
pandas
scipy

Unverifiable Dependency: scipy has 4 known advisory(ies) (CVE-2013-4251 (SciPy creates insecure temporary directories); CVE-2013-4251 (The scipy.weave component in SciPy before 0.12.1 creates insecure temporary dire); CVE-2023-25399 (A refcounting issue which leads to potential memory leak was discovered in scipy) +1 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
90% confidence
Finding

Scipy has multiple known advisories, and without a pinned version there is no reliable way to determine whether an installation is exposed. This creates avoidable supply-chain uncertainty and may result in vulnerable code being introduced into a regulated clinical data processing environment.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

The dependency list leaves numpy unpinned, so installations may resolve to different versions over time, including releases with security defects or breaking changes. In a clinical-data processing skill, this undermines reproducibility and makes it impossible to verify whether a deployed environment is using a safe version.

Content

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

text
numpy
pandas
scipy

Unverifiable Dependency: numpy has 16 known advisory(ies) (CVE-2014-1859 (Numpy arbitrary file write via symlink attack); CVE-2021-41495 (NumPy NULL Pointer Dereference); CVE-2021-33430 (NumPy Buffer Overflow (Disputed)) +13 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
92% confidence
Finding

Numpy has known advisories, and because no version is specified, there is no way to determine whether the installed package is affected. This turns known vulnerability intelligence into an unresolvable risk and weakens assurance for environments processing potentially sensitive clinical datasets.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
97% confidence
Finding

The unpinned pandas dependency allows uncontrolled version selection at install time, which can introduce vulnerable or incompatible releases without any manifest change. For software handling regulated clinical data, that creates supply-chain and reproducibility risk that is especially problematic for auditability.

Content

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

text
numpy
pandas
scipy

Static analysis

No suspicious patterns detected.