Back to skill

Security audit

TV Signal Screener

Security checks for vulnerabilities and agentic risk

Overview

This skill is a coherent TradingView market screener with some reliability and dependency-hardening caveats, but no evidence of hidden access, credential theft, persistence, or destructive behavior.

Install only if you are comfortable with PyPI dependencies being resolved at install time. Treat screening output as informational, check stderr warnings, and verify YAML strategies carefully because invalid computed expressions may leave data less filtered than expected.

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
scripts/requirements.txt:1
Finding

Unpinned Third-Party Dependencies Create a Supply-Chain Risk

Content
View full analysis

Vulnerability Details

File Location: scripts/requirements.txt:1-4 and install.sh:47-49
Vulnerability Type: Unpinned dependency installation without integrity verification
Risk Level: Medium

Vulnerable Code

scripts/requirements.txt:1-4:

text
tvscreener>=0.2.0
pandas>=2.0.0
pyyaml>=6.0
pytest>=7.0.0

install.sh:47-49:

bash
# Install dependencies
echo "Installing dependencies..."
"$PIP" install --no-user -q -r "$REQ_FILE"

Technical Analysis

Every dependency uses an unrestricted lower-bound constraint. The installer therefore downloads whichever newer version satisfies the constraint at installation time, including unaudited future releases and changing transitive dependencies.

The installation also lacks package hashes or another integrity-verification mechanism. Python package installation may execute build-system or setup hooks with the privileges of the user running the installer. Consequently, compromise of a direct or transitive dependency could result in local code execution during installation or subsequent imports.

Installing pytest as a production dependency unnecessarily expands the runtime dependency graph and supply-chain attack surface.

Attack Path

  1. An attacker compromises a permitted direct dependency or one of its transitive dependencies and publishes a malicious version satisfying the declared lower bound.
  2. A user follows the documented setup procedure and runs install.sh.
  3. pip resolves the latest compatible dependency versions from its configured package index.
  4. Because no exact version or hash is required, the compromised package is accepted.
  5. Malicious build hooks execute during installation, or malicious package code executes when imported by the Skill.

Impact Assessment

Successful exploitation could execute arbitrary code with the privileges of the user installing or running the Skill. That code could access ...[truncated 316 chars]

Remediation
View remediation

Remediation Suggestions

  1. Pin every direct dependency to an exact, reviewed version.
  2. Generate a lock file that includes all transitive dependencies.
  3. Record trusted distribution hashes and install with pip --require-hashes.
  4. Separate development dependencies such as pytest from production dependencies.
  5. Use a controlled package index or repository allowlist where practical.
  6. Add automated dependency vulnerability and provenance scanning.
  7. Review and deliberately update the lock file on a defined maintenance schedule rather than resolving arbitrary future versions during installation.

T09 · Insecure Skill Coding Practices

Note
Location
scripts/signal_types.py:94
Finding

Computed Expression Failures Bypass Signal Filtering

Content
View full analysis

Vulnerability Details

File Location: scripts/signal_types.py:94-104
Vulnerability Type: Fail-open error handling
Risk Level: Low

Vulnerable Code

python
expr = config["expr"]
validate_expression(expr)
# Resolve field names in expression to actual DataFrame column names
resolved_expr = _resolve_expr_columns(df, expr)
try:
    mask = df.eval(resolved_expr)
    return df[mask].copy()
except Exception as e:
    print(f"Warning: Expression '{resolved_expr}' failed: {e}", file=sys.stderr)
    return df

This behavior is explicitly expected by scripts/tests/test_signal_engine.py:97-100:

python
def test_expression_invalid_returns_original(indicator_df):
    config = {"expr": "NONEXISTENT > 0"}
    result = apply_expression(indicator_df, config)
    assert len(result) == len(indicator_df)  # Returns unchanged on error

Technical Analysis

When expression evaluation fails, the function logs a warning and returns the complete input DataFrame. This is fail-open behavior: the configured security or correctness condition is silently omitted rather than causing processing to stop or produce no matches.

The initial validator only checks allowed characters and a small blocked-keyword set. Expressions can therefore pass that validation but fail later because of nonexistent columns, unsupported operations, incompatible data types, or invalid pandas evaluation semantics.

A user-supplied YAML file can trigger this behavior because signal_engine.py accepts an arbitrary local path through --signal. Although this is not arbitrary code execution, it permits bypass of the intended computed filter and compromises screening-result integrity.

Attack Path

  1. An attacker supplies or modifies a YAML strategy containing an expression that passes validate_expression.
  2. The user invokes the signal engine with that strategy.
  3. Market data is fetched normally.
  4. The expression ...[truncated 787 chars]
Remediation
View remediation

Remediation Suggestions

  1. Fail closed: raise a validation exception or return an empty DataFrame when expression evaluation fails.
  2. Stop processing the entire signal configuration and return a nonzero exit status for invalid expressions.
  3. Validate all referenced identifiers against the actual DataFrame columns before evaluation.
  4. Replace the regular-expression validator with an explicit expression grammar or parsed abstract syntax tree that permits only documented operators, numeric constants, approved columns, and approved aggregate methods.
  5. Ensure the evaluation result is a Boolean Series aligned with the DataFrame index before applying it as a mask.
  6. Add negative tests asserting that invalid expressions terminate processing rather than returning the original data.
  7. Surface failures prominently in standard output or structured output so that results cannot be mistaken for successful matches.
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
Findings (13)

Tp4

High
Category
MCP Tool Poisoning
Confidence
92% confidence
Finding

The code does align with part of the description: it screens TradingView market data across asset classes, applies pre-filters, and formats results. However, the declared description specifically mentions 'pandas computed signals' and 'YAML-driven strategies,' neither of which appears in this chunk. Instead, filters are provided via a JSON CLI argument, and the code simply fetches, sorts, limits, and prints results as markdown. This is a meaningful description-to-behavior mismatch because notable advertised capabilities are absent from the supplied code.

Content

No source excerpt is available for this finding.

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · references/computed-signals-guide.md (reported line 191)May include surrounding context.

import os # No imports eval("malicious code") # No eval/exec PRICE.apply(lambda x: x * 2) # No lambda open("/etc/passwd") # No file operations

text

## Common Patterns

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The docstring says this function will 'Detect when fast field is above/below slow field', which implies identifying a crossover event. The code only filters rows where one column is currently greater than, less than, or not equal to the other, with no prior-row comparison or edge detection, so it does not actually detect a crossing.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The phrase 'crosses a threshold value' suggests transition-aware logic, such as detecting when a value moves from one side of a threshold to the other. The implementation simply applies operators like >, >=, <, and == to the current row values, without tracking previous state or detecting any crossing event.

Content

No source excerpt is available for this finding.

Intent-Code Divergence

Low
Category
Not specified by scanner
Confidence
81% confidence
Finding

The guide states that custom logic is evaluated via pandas eval() with whitelist validation and explicitly restricts expressions to a narrow operator/method set. However, surrounding examples blur that contract by presenting non-literal computed values and free-form expressions as configuration, which undermines the claimed constraints and creates documentation-to-implementation intent ambiguity.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
96% confidence
Finding

The dependency specification for tvscreener uses a lower-bound constraint only, which allows future unreviewed versions to be installed. This weakens build reproducibility and can silently introduce breaking changes or vulnerable releases through the software supply chain.

Content

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

text
tvscreener>=0.2.0
pandas>=2.0.0
pyyaml>=6.0
pytest>=7.0.0

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
98% confidence
Finding

The pandas dependency is not pinned to an exact version, so installations may resolve to different releases over time. In a data-processing skill that relies on pandas, this increases supply-chain risk and makes it harder to ensure that only vetted versions are used.

Content

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

text
tvscreener>=0.2.0
pandas>=2.0.0
pyyaml>=6.0
pytest>=7.0.0

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
76% confidence
Finding

Because pandas is not pinned, it is impossible to verify from this manifest whether the installed version is affected by known advisories. The disputed advisory lowers certainty of direct exploitability here, but the lack of version control still prevents accurate risk assessment and safe reproduction.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
99% confidence
Finding

PyYAML is specified with only a minimum version, which permits resolver selection of any later release without review. Because this skill explicitly mentions YAML-driven strategies, unsafe or vulnerable parser versions would be especially relevant and could increase exposure to deserialization or parsing-related issues.

Content

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

text
tvscreener>=0.2.0
pandas>=2.0.0
pyyaml>=6.0
pytest>=7.0.0

Unverifiable Dependency: pyyaml has 8 known advisory(ies) (CVE-2019-20477 (Deserialization of Untrusted Data in PyYAML); CVE-2020-1747 (Improper Input Validation in PyYAML); CVE-2020-14343 (Improper Input Validation in PyYAML) +5 more), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
95% confidence
Finding

PyYAML has multiple known advisories, and the manifest does not pin a version, so the environment could install a vulnerable release without visibility. This is more concerning in this skill because YAML is part of the advertised feature set, making parser safety directly relevant to expected usage.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
94% confidence
Finding

pytest is unpinned, so the test dependency may resolve to arbitrary newer versions and reduce reproducibility. Although this is typically less dangerous than an unpinned runtime dependency, it can still introduce vulnerable or compromised packages into development or CI environments.

Content

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

text
tvscreener>=0.2.0
pandas>=2.0.0
pyyaml>=6.0
pytest>=7.0.0

Unverifiable Dependency: pytest has 2 known advisory(ies) (CVE-2025-71176 (pytest has vulnerable tmpdir handling); CVE-2025-71176 (pytest has vulnerable tmpdir handling)), but the manifest does not pin a version, so it is unknown whether the installed release is affected

Low
Category
Supply Chain
Confidence
73% confidence
Finding

pytest has known advisories, but the manifest does not constrain the exact version, so it cannot be verified whether the installed package is affected. Since pytest is generally a development dependency, the primary risk is to CI or developer environments rather than production runtime.

Content

No source excerpt is available for this finding.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Confidence
50% confidence
Finding

Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Content

Scanner excerpt · scripts/screener_constants.py (reported line 42)May include surrounding context.

python
"""
    name_upper = field_name.strip().upper()
    try:
        return getattr(field_enum, name_upper)
    except AttributeError:
        # Try search as fallback
        results = field_enum.search(field_name)