Back to skill

Security audit

timeplus-app-builder

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Timeplus app-building guide, but it includes server-side app installation patterns with under-scoped handling of secrets, executable templates, mutable dependencies, and network listeners.

Review this skill before installing in production. Use it only with trusted app packages and trusted install-time config, prefer HTTPS for non-local Timeplus endpoints, pin Python dependencies, avoid sending secrets through plaintext or queryable resource definitions, validate and quote template values by context, and do not bind inputs to 0.0.0.0 unless network access is explicitly restricted.

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

T09 · Insecure Skill Coding Practices

Error
Location
SKILL.md:735
Finding
Unsafe Configuration Interpolation Enables Python and SQL Injection## Vulnerability Details **File Location**: `SKILL.md`, lines 641-650, 735-742, and 800-805 **Vulnerability Type**: Configuration-driven code injection **Risk Level**: High ### Vulnerable Code ```sql CREATE EXTERNAL STREAM IF NOT EXISTS {{ .DB }}.raw_feed (msg string) SETTINGS type='websocket', url='{{ .Config.websocket_url }}'; ``` ```sql CREATE OR REPLACE FUNCTION notify_slack(channel string, message string) RETURNS bool LANGUAGE PYTHON AS $$ import requests def notify_slack(channel, message): url = '{{ .Config.slack_webhook_url }}' requests.post(url, json={'channel': channel, 'text': message}) return [True] * len(channel) $$; ``` ```sql CREATE ALERT IF NOT EXISTS {{ .DB }}.price_spike_alert BATCH 10 EVENTS WITH TIMEOUT 5s LIMIT 1 ALERTS PER 10s CALL {{ .DB }}.notify_slack AS SELECT product_id, price, _tp_time FROM {{ .DB }}.coinbase_tickers WHERE price > {{ .Config.alert_threshold }}; ``` ### Technical Analysis The Skill recommends directly inserting install-time configuration values into SQL string literals, executable Python source, and unquoted SQL expressions. No context-specific escaping, validation, or strict allowlisting is applied. The most severe case is `slack_webhook_url`. Because it is placed directly inside a single-quoted Python literal, a value containing a quote and additional Python syntax can terminate the intended string and modify the generated UDF body. The generated code is subsequently registered and executed by the Timeplus server. The `websocket_url` value is similarly inserted into a SQL literal without SQL-literal escaping. A malicious value could terminate the literal and alter the generated DDL if the surrounding parser permits the resulting syntax. The unquoted `alert_threshold` expression can also change the alert predicate or inject additional SQL expression syntax unless the manifest strictly enforces an integer or float type b ...[truncated 1229 chars]
Remediation
## Remediation Suggestions - Do not construct executable Python source from raw configuration values. - Pass webhook URLs and credentials through runtime parameters or a protected secret reference rather than embedding them in a UDF body. - Apply context-specific SQL literal escaping for values inserted into SQL strings. - Declare numeric values such as `alert_threshold` with strict numeric types and reject values that do not match a canonical numeric representation. - Validate URLs using an allowlist of approved schemes and, where possible, approved destination hosts. - Reject control characters, unexpected quotes, template delimiters, and multiline input where they are not necessary. - Generate structured resource definitions through safe APIs rather than string concatenation whenever supported. - Execute generated UDFs in a restricted sandbox without filesystem access, unrestricted network access, or unnecessary service credentials. - Add tests using quote characters, backslashes, newlines, and code fragments to verify that configuration values cannot change generated syntax.

T08 · Insecure Dependencies

Warning
Location
SKILL.md:126
Finding
Unpinned Python Dependency Installation Creates a Supply-Chain Risk## Vulnerability Details **File Location**: `SKILL.md`, lines 126-129 and 552-555 **Vulnerability Type**: Unpinned third-party dependencies **Risk Level**: Medium ### Vulnerable Code ```yaml python_packages: - json5>=0.9.6 - websocket-client>=1.4.0 ``` The Skill also states that the installer installs these packages before executing any DDL: ```text Declare packages in `python_packages` in the manifest — the installer installs them and waits for completion before running any DDL. ``` ### Technical Analysis The dependency declarations use open-ended minimum-version constraints rather than immutable versions. A future installation may therefore retrieve a newer package release than the one originally reviewed. No package hashes, trusted repository restrictions, lock file, or signature-verification requirements are specified. Because package installation occurs on the server before DDL execution, a compromised upstream release, dependency-confusion event, or malicious transitive dependency could introduce executable code into the Timeplus environment. ### Attack Path 1. An app manifest declares one of the open-ended dependency ranges. 2. A package index later serves a newer compromised version, or dependency resolution is redirected to an untrusted source. 3. The Timeplus installer resolves the mutable range during app installation. 4. The compromised package is downloaded and installed into the server-side Python environment. 5. Malicious package code executes during installation or when imported by an external stream or UDF. ### Impact Assessment The resulting privileges depend on how the package installer and Python runtime are isolated. Potential impact includes server-side code execution, unauthorized network access, reading files available to the service account, theft of application credentials, and compromise of other applications sharing the same Python environment.
Remediation
## Remediation Suggestions - Pin every direct dependency to an exact reviewed version. - Use a lock file that records the complete transitive dependency graph. - Require cryptographic hashes for all downloaded distributions. - Restrict resolution to an approved internal package registry or an explicitly configured trusted index. - Disable dependency resolution from arbitrary additional indexes. - Scan packages and transitive dependencies before allowing deployment. - Install packages in an isolated, least-privilege environment dedicated to the app. - Avoid sharing a mutable global Python environment across unrelated apps. - Establish a controlled update process in which new versions are reviewed, tested, and deliberately pinned.

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:470
Finding
AWS Credentials Are Stored in Plaintext in a Queryable Named Collection## Vulnerability Details **File Location**: `SKILL.md`, lines 470-474 and 522-529 **Vulnerability Type**: Plaintext credential exposure **Risk Level**: Medium ### Vulnerable Code ```sql CREATE NAMED COLLECTION IF NOT EXISTS aws_cost_creds AS init_function_parameters = '{"access_key_id":{{ .Config.aws_access_key_id | quote }},"secret_access_key":{{ .Config.aws_secret_access_key | quote }}}' NOT OVERRIDABLE; ``` The Skill explicitly documents the residual exposure: ```text `system.named_collections` still exposes the blob. Reading the `collection` map or `create_query` column from `system.named_collections` returns the raw JSON, since Proton only auto-masks the literal key `password`. Restrict that privilege to operators. ``` ### Technical Analysis The named-collection pattern prevents credentials from appearing in `SHOW CREATE EXTERNAL STREAM`, but it does not securely store them. The AWS access key and secret access key remain embedded as plaintext JSON in the named collection. The documented masking behavior only recognizes the literal key `password`; consequently, fields named `access_key_id` and `secret_access_key` remain visible through relevant `system.named_collections` columns. This relocates the credential exposure rather than eliminating it. The credentials are also copied into module-level Python globals for each read session. Credential rotation requires dropping and recreating the external stream because the named-collection values are snapshotted at stream creation. ### Attack Path 1. An app is installed with AWS credentials supplied through configuration. 2. The installer renders the credentials into `init_function_parameters`. 3. The named collection stores the resulting JSON blob. 4. A user or compromised component with permission to query the relevant system-table fields selects `collection` or `create_query`. 5. The plaintext AWS access key and secret access key are recovered. ...[truncated 618 chars]
Remediation
## Remediation Suggestions - Store credentials in a dedicated external secret manager and place only an opaque secret reference in the app definition. - Retrieve short-lived credentials at runtime using a workload identity or role-based mechanism instead of static AWS access keys. - If named collections must be used, encrypt sensitive values at rest and ensure decryption is available only to the intended runtime. - Extend masking and redaction to all secret fields, including `secret_access_key`, access tokens, API keys, and private keys. - Deny access to sensitive `system.named_collections` columns by default and expose only the collection name for discovery. - Use a dedicated least-privilege AWS identity for each app. - Implement credential rotation that does not require prolonged use of snapshotted static credentials. - Audit access to system-table fields that can reveal resource definitions or secret-bearing values.

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:819
Finding
Input Service Example Binds to All Network Interfaces by Default## Vulnerability Details **File Location**: `SKILL.md`, lines 819-836 **Vulnerability Type**: Unnecessary network exposure **Risk Level**: Medium ### Vulnerable Code ```sql CREATE INPUT IF NOT EXISTS {{ .DB }}.syslog_in SETTINGS type='syslog', target_stream='{{ .DB }}.raw_logs', tcp_port={{ .Config.syslog_port }}, listen_host='0.0.0.0' COMMENT 'Syslog receiver'; ``` The accompanying guidance states: ```text `listen_host` — address to bind (use `'0.0.0.0'` for all interfaces) ``` ### Technical Analysis Binding to `0.0.0.0` makes the input listener reachable through every network interface permitted by host and network controls. This exceeds minimum privilege when ingestion is intended only from the local host, a sidecar, or a private management network. The example does not place authentication, TLS, source allowlisting, firewall restrictions, or rate limiting next to the all-interface binding recommendation. Users who copy the example may unintentionally expose the ingestion endpoint to untrusted networks. ### Attack Path 1. An operator creates the input using the documented example. 2. The service binds the configured port on every host interface. 3. A firewall, container port mapping, or cloud security group permits access from an untrusted network. 4. A remote attacker connects to the listener. 5. The attacker submits arbitrary or high-volume protocol messages. 6. The messages consume processing or storage resources and may contaminate downstream dashboards, alerts, and analytics. ### Impact Assessment An exposed listener may permit unauthorized event ingestion, log forgery, analytics corruption, alert manipulation, resource exhaustion, or denial of service. The precise impact depends on the selected protocol, network controls, parser robustness, target-stream retention, and downstream automation triggered by received events.
Remediation
## Remediation Suggestions - Default `listen_host` to `127.0.0.1` or a specifically configured trusted interface. - Require users to opt in explicitly before binding to all interfaces. - Document authentication and TLS requirements for every network input protocol. - Restrict inbound traffic with host firewalls, container network policies, and cloud security groups. - Apply source-address allowlists where protocol-level authentication is unavailable. - Add connection, message-size, ingestion-rate, and storage quotas. - Run the listener under a dedicated least-privilege service identity. - Monitor failed connections, unexpected source addresses, ingestion spikes, and malformed messages. - Clearly warn that `0.0.0.0` exposes the listener on all available interfaces.
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (2)

Missing User Warnings

Medium
Confidence
91% confidence
Finding
This markdown file instructs users to POST `.tpapp` files and `config[...]` values to `http://localhost:8000/.../apps/install`, and the manifest examples include sensitive fields such as `api_key`. While the behavior is central to the skill, the document does not explicitly warn users that install requests may transmit package contents and secrets over plaintext HTTP if used as shown.

External Transmission

Medium
Category
Data Exfiltration
Content
import requests
def notify_slack(channel, message):
    url = '{{ .Config.slack_webhook_url }}'
    requests.post(url, json={'channel': channel, 'text': message})
    return [True] * len(channel)
$$;
```
Confidence
70% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Static analysis

No suspicious patterns detected.