Back to skill

Security audit

OData Service

Security checks for vulnerabilities and agentic risk

Overview

The skill is mostly coherent for OData work, but it ships unnecessary Python bytecode that should be reviewed or removed before installation.

Install only if you are comfortable with a skill that can read from and modify configured OData services using your environment-provided credentials. Review or remove the bundled __pycache__ bytecode before use, configure profiles with environment-variable names rather than secret values, and require explicit confirmation for any write, delete, batch, action, or stream update.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Behavioral ASTexec() Call, eval() Call, Dynamic Import
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (25)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding
The declared description presents a fully functional OData client/integration skill that can communicate with OData services and perform both read and write protocol operations. The supplied code does none of that. It only manages local service profiles: parses CLI arguments, validates profile fields, stores profile metadata in a config file, lists and shows profiles, changes the default profile, and removes profiles. Although the profiles contain OData-related fields like service root and version, this is only preparatory configuration and not actual OData service interaction. The code also writes to a local config file, which is a materially different behavior from the declared service-operation focus. Therefore the description does not accurately represent this code chunk.

Tp4

High
Category
MCP Tool Poisoning
Confidence
97% confidence
Finding
The declared description presents a comprehensive OData v4 client capable of interacting with OData services across read and write scenarios. The supplied code chunk, however, is only a test module focused on configuration profile persistence and argument resolution for a script named odata_profiles.py. It does not perform network requests, discover models, query entities, modify data, invoke OData actions/functions, or handle batch/asynchronous requests. While profile/config support could be a supporting detail for an OData client, this chunk’s actual purpose is materially narrower and different from the declared end-user functionality, so the description does not accurately represent what this code actually does.

Ae1

High
Category
analysis-evasion
Content
config.py list` and use its default profile. Read [references/configuration.md](references/configuration.md) when no endpoint is available or the user wants to
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
, navigation, containment, capabilities, addressing, or ordinary queries, read [references/model-and-query.md](references/model-and-query.md).
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
, or `$ref` relationship changes, read [references/writes-and-relationships.md](references/writes-and-relationships.md).
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
s, preferences, or `respond-async`, read [references/operations-batch-async.md](references/operations-batch-async.md).
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- For media entities, stream properties, delta tracking, or synchronization, read [references/streams-and-delta.md](references/streams-and-delta.md).
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
- For response interpretation, errors, retries, versions, and normative links, read [references/protocol-and-errors.md](references/protocol-and-errors.md).
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
The dependency-free `scripts/odata_request.py` can issue guarded OData HTTP requests, inspect service/metadata endpoints, and traverse JSON collection pages. Ru
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Skill ships a __pycache__ directory that normal discovery skips

High
Category
Supply Chain
Content
Python may load .pyc from this directory even when decoy .py sources look clean (PEP 552 UNCHECKED_HASH).
Confidence
95% confidence
Finding
Skill ships Python bytecode (__pycache__/ or .pyc/.pyo). Discovery skips these paths, so malicious bytecode can score SAFE while decoy sources look clean.

Skill ships Python bytecode (.pyc/.pyo) that normal analysis skips

High
Category
Supply Chain
Content
Bytecode is excluded from content analysis; a malicious .pyc can execute while source decoys remain clean.
Confidence
95% confidence
Finding
Skill ships Python bytecode (__pycache__/ or .pyc/.pyo). Discovery skips these paths, so malicious bytecode can score SAFE while decoy sources look clean.

Skill ships a __pycache__ directory that normal discovery skips

High
Category
Supply Chain
Content
Python may load .pyc from this directory even when decoy .py sources look clean (PEP 552 UNCHECKED_HASH).
Confidence
95% confidence
Finding
Skill ships Python bytecode (__pycache__/ or .pyc/.pyo). Discovery skips these paths, so malicious bytecode can score SAFE while decoy sources look clean.

Skill ships Python bytecode (.pyc/.pyo) that normal analysis skips

High
Category
Supply Chain
Content
Bytecode is excluded from content analysis; a malicious .pyc can execute while source decoys remain clean.
Confidence
95% confidence
Finding
Skill ships Python bytecode (__pycache__/ or .pyc/.pyo). Discovery skips these paths, so malicious bytecode can score SAFE while decoy sources look clean.

Lp3

Medium
Category
MCP Least Privilege
Confidence
85% confidence
Finding
The skill text directs use of shell, network, environment variables, and local configuration/scripts but does not declare any explicit tool scope or permissions boundary. In an agent environment, missing scope declarations can let the skill access broader capabilities than users or reviewers expect, increasing the chance of unintended network calls, config changes, or secret exposure.

External Transmission

Medium
Category
Data Exfiltration
Content
Bearer token profile:

```text
python scripts/odata_config.py set production --service-root https://api.example.com/odata/ --odata-version 4.0 --bearer-env PROD_ODATA_TOKEN --default
```

API-key/custom-header profile:
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
API-key/custom-header profile:

```text
python scripts/odata_config.py set warehouse --service-root https://data.example.com/odata/ --header-env X-API-Key=WAREHOUSE_API_KEY
```

Basic authentication profile:
Confidence
50% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
path = pathlib.Path(temp_dir) / "services.json"
            value = profiles.empty_config()
            value["profiles"]["production"] = {
                "service_root": "https://api.example.test/odata",
                "odata_version": "4.01",
                "bearer_env": "PROD_ODATA_TOKEN",
                "headers_from_env": {"X-Tenant": "PROD_TENANT"},
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
path = pathlib.Path(temp_dir) / "services.json"
            value = profiles.empty_config()
            value["profiles"]["production"] = {
                "service_root": "https://api.example.test/odata",
                "odata_version": "4.01",
                "bearer_env": "PROD_ODATA_TOKEN",
                "headers_from_env": {"X-Tenant": "PROD_TENANT"},
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
path = pathlib.Path(temp_dir) / "services.json"
            value = profiles.empty_config()
            value["profiles"]["production"] = {
                "service_root": "https://api.example.test/odata",
                "odata_version": "4.01",
                "bearer_env": "PROD_ODATA_TOKEN",
                "headers_from_env": {"X-Tenant": "PROD_TENANT"},
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
path = pathlib.Path(temp_dir) / "services.json"
            value = profiles.empty_config()
            value["profiles"]["production"] = {
                "service_root": "https://api.example.test/odata",
                "odata_version": "4.01",
                "bearer_env": "PROD_ODATA_TOKEN",
                "headers_from_env": {"X-Tenant": "PROD_TENANT"},
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

External Transmission

Medium
Category
Data Exfiltration
Content
path = pathlib.Path(temp_dir) / "services.json"
            value = profiles.empty_config()
            value["profiles"]["production"] = {
                "service_root": "https://api.example.test/odata",
                "odata_version": "4.01",
                "bearer_env": "PROD_ODATA_TOKEN",
                "headers_from_env": {"X-Tenant": "PROD_TENANT"},
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
"headers_from_env": parse_headers(args.header_env),
    }
    for field in ("bearer_env", "basic_user_env", "basic_password_env"):
        option = getattr(args, field)
        if option:
            profile[field] = option
    value["profiles"][args.name] = validate_profile(args.name, profile)
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Missing User Warnings

Low
Confidence
88% confidence
Finding
The `remove` command deletes a named profile and persists the modified configuration with `save_config`, which is a potentially destructive file write. While the command name suggests deletion, this function contains no confirmation prompt or in-code warning/comment about the irreversible effect on stored profile definitions.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
args.service_root = profile["service_root"]
    args.odata_version = args.odata_version or profile.get("odata_version", "4.0")
    for field in ("bearer_env", "basic_user_env", "basic_password_env"):
        if not getattr(args, field, None):
            setattr(args, field, profile.get(field))
    configured_headers = [f"{name}={env_name}" for name, env_name in profile.get("headers_from_env", {}).items()]
    args.header_env = configured_headers + list(getattr(args, "header_env", []))
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Dynamic attribute access via getattr()

Low
Category
Dangerous Code Execution
Content
parser = build_parser()
    args = parser.parse_args()
    for name in ("timeout", "max_response_bytes", "max_pages", "max_items"):
        value = getattr(args, name, 1)
        if value <= 0:
            parser.error(f"--{name.replace('_', '-')} must be positive")
    try:
Confidence
50% confidence
Finding
Dynamic getattr() with a non-literal attribute name can access arbitrary object attributes, potentially bypassing access controls.

Static analysis

Detected: suspicious.dynamic_code_execution, suspicious.exposed_secret_literal

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
tests/test_odata_profiles.py:12

Dynamic code execution detected.

Critical
Code
suspicious.dynamic_code_execution
Location
tests/test_odata_request.py:17

File appears to expose a hardcoded API secret or token.

Critical
Code
suspicious.exposed_secret_literal
Location
references/configuration.md:26