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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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

Content
View full analysis

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
View 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
Category
Not specified by scanner
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.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
70% confidence
Finding

Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Content

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

import requests def notify_slack(channel, message): url = '{{ .Config.slack_webhook_url }}' requests.post(url, json={'channel': channel, 'text': message}) return [True] * len(channel) $$;

text

Static analysis

No suspicious patterns detected.