Back to skill

Security audit

TPM Copilot

Security checks for vulnerabilities and agentic risk

Overview

This TPM automation skill is mostly purpose-aligned, but it handles service credentials and sends project data through configurable endpoints without enough safeguards.

Review and harden this before installing in a work environment: store tokens outside committed config files, restrict config file permissions, validate Jira and webhook destinations, use least-privileged service tokens, and run ticket creation or report delivery only after checking the destination and content.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/add-program.sh:22
Finding

Program Name Path Traversal and Unsafe JSON Construction

Content
View full analysis
"$PROG_DIR/config.json" << EOF { "name": "$DISPLAY_NAME", "slug": "$NAME", "tracker": "$TRACKER", "workstreams": [ { "name": "$DISPLAY_NAME", "jira_project": "$PROJECT", "linear_team": "$PROJECT", "github_repos": [$([ -n "$REPO" ] && echo "\"$REPO\"" || echo "")], "exec": [], "slack_channel": "" } ] } EOF ``` ### Technical Analysis The `--name` value is concatenated directly into `PROG_DIR` without validating that it is a safe single path component. Shell quoting prevents word splitting and shell metacharacter execution, but it does not neutralize filesystem traversal sequences such as `../`. The script subsequently creates directories and writes `config.json` through the resulting path. A name containing traversal components can therefore escape `$BASE_DIR/programs`. The impact remains limited by the operating-system privileges of the user running the Skill. The same program-name path construction is used by: - `scripts/status-report.sh:20` - `scripts/risk-radar.sh:20` - `scripts/dependency-map.sh:20` - `scripts/meeting-prep.sh:18` - `scripts/dashboard.sh:10` Those scripts can consequently access a directory outside the intended `programs` root if a crafted program argument resolves to an existing directory. In addition, `NAME`, `DISPLAY_NAME`, `TRACKER`, `PROJECT`, and `REPO` are inserted into a JSON heredoc without JSON escaping. Quotes, backslashes, control characters, or newlines can produce invalid JSON or alter the generated JSON structure. ### Attack Path 1. An attacker persuades an authorized user or automation process to invoke `add-program.sh` with a crafted name such as `../../attacker-selected-directory ...[truncated 1138 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/dependency-map.sh:64
Finding

Jira Basic Authentication Credentials Can Be Sent to an Unvalidated Endpoint

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/dependency-map.sh:144
Finding

Project and Risk Data Can Be Posted to an Arbitrary Configured Webhook

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/init-workspace.sh:7
Finding

Plaintext Service Credentials Are Stored Without Enforced Restrictive File Permissions

Content
View full analysis
"$BASE_DIR/config.json" << 'EOF' { ... "api_token": "", ... "api_key": "" } EOF ``` The initialized configuration is intended to hold credentials for services including Jira, Linear, Slack, and Resend. No restrictive `umask` or explicit `chmod 600` is applied when the file is created. ### Technical Analysis Shell redirection creates `config.json` using permissions derived from the process umask. Under a permissive umask, the file may be readable by other users on the same system. Although the initial template may contain empty values, the documented workflow uses this file for live API tokens, keys, and webhook URLs. Consequently, its security properties must be appropriate for secret storage rather than ordinary configuration storage. The plaintext format is not itself unexpected for a local configuration file, but failing to enforce owner-only access violates least-privilege expectations for credentials. ### Attack Path 1. A user initializes the TPM workspace while operating under a permissive umask. 2. `config.json` is created without an explicit owner-only mode. 3. The user later populates the file with Jira, Linear, Slack, or Resend credentials. 4. Another local account with filesystem access reads the configuration. 5. The attacker uses the exposed credentials against the associated external services. ### Impact Assessment A local attacker may obtain service tokens, API keys, or webhook URLs. The resulting remote privileges are limited to those granted to each exposed credential but may include reading Jira or Linear project data, creating Jira issues, sending email through Resend, or posting to Slack. This issue does not independently create a local privilege escalation. Exploitat ...[truncated 109 chars]
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Trigger AbuseOverly Broad Trigger, Shadow Command Trigger, Keyword Baiting Trigger
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
  • MCP Tool PoisoningHidden Instructions, Unicode Deception, Parameter Description Injection
Findings (38)

Tp4

High
Category
MCP Tool Poisoning
Confidence
98% confidence
Finding

The declared description promises a full-featured TPM/PM automation system with integrations, analytics, reporting, and workflow support. The supplied code chunk does none of that. It only parses arguments, creates local directories, and writes a starter config.json file with placeholder settings for tracker/project/repo metadata. While this scaffolding could support a larger TPM tool, this specific code chunk's actual behavior is limited to setup/initialization and does not implement the described capabilities. Therefore, the description does not accurately represent what this code chunk actually does.

Content

No source excerpt is available for this finding.

Tp4

High
Category
MCP Tool Poisoning
Confidence
94% confidence
Finding

The code is related to one small part of the declared domain—meeting workflow support via extracting action items from notes—but it does not substantiate the much broader declared description. Its primary behavior is a local utility for parsing a markdown meeting-notes file and appending extracted actions to a JSON file. There are no integrations with Jira, Linear, GitHub, or calendars; no generation of status reports, dashboards, dependency maps, or sprint metrics; and no risk/blocker analysis beyond generic action capture. While this could be a supporting component within a larger TPM/PM system, the supplied code chunk itself materially underdelivers relative to the declared purpose, so the description does not accurately represent what this chunk actually does.

Content

No source excerpt is available for this finding.

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · references/jira-setup.md (reported line 47)May include surrounding context.

Finding Board IDs

bash
curl -s "https://yourcompany.atlassian.net/rest/agile/1.0/board" \
  -u "email:token" | python3 -m json.tool

External Script Fetching

High
Category
Supply Chain
Confidence
90% confidence
Finding

Remote code is downloaded and executed. This bypasses code review and could introduce malicious code.

Content

Scanner excerpt · references/linear-setup.md (reported line 23)May include surrounding context.

3. Finding Team IDs

bash
curl -s https://api.linear.app/graphql \
  -H "Authorization: lin_api_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ teams { nodes { id name } } }"}' | python3 -m json.tool

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
88% confidence
Finding

The skill advertises capabilities that require sensitive operations (environment access, file read/write, network, and shell) but does not declare any explicit tool scope or permissions boundaries. This creates an over-privileged trust model where an agent could access credentials, modify local files, or contact external services without clear user-visible constraints.

Content

No source excerpt is available for this finding.

Vague Triggers

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The invocation description is broad enough to trigger the skill for generic PM or TPM requests, increasing the chance that it runs in contexts the user did not explicitly intend. In a skill with potential network, shell, and data-access capabilities, over-broad triggering can expose project data or cause external actions on overly general prompts.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The skill description states that it connects to Jira, Linear, GitHub, Slack, email, Confluence, and calendars, but it does not prominently warn users that potentially sensitive project, personnel, and meeting data may be transmitted to external services. Without clear disclosure and consent boundaries, users may unknowingly expose confidential schedules, internal status, ticket contents, or stakeholder information.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation instructs users to place a Jira API token directly in a local JSON config file without any guidance on secure storage, file permissions, or avoiding commits. In a TPM/PM automation skill that aggregates access to Jira and other systems, this increases the chance of credential leakage through source control, backups, local compromise, or accidental sharing.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
89% confidence
Finding

The environment-variable example exposes a sensitive API token with no warning about shell history, shared terminals, process inspection, or persistent shell profile files. While environment variables are often better than config files, presenting them without safe-handling guidance can still lead to accidental disclosure of Jira credentials.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
97% confidence
Finding

The example curl command places Jira credentials inline with the -u "email:token" argument, which can expose secrets via shell history, process listings, terminal logs, and copy-paste artifacts. Because this skill is meant to be used operationally by program managers, the docs may be followed by less security-focused users, increasing practical leakage risk.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
95% confidence
Finding

The documentation instructs users to place a personal Linear API key directly in a JSON config file or shell environment without any warning about secret sensitivity, least-privilege handling, file permissions, or exclusion from source control. This can lead to accidental credential exposure through committed config files, shared screenshots, shell history, process inspection, or insecure workstation practices, especially in a TPM automation tool that aggregates multiple external systems.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · references/linear-setup.md (reported line 23)May include surrounding context.

3. Finding Team IDs

bash
curl -s https://api.linear.app/graphql \
  -H "Authorization: lin_api_xxxxx" \
  -H "Content-Type: application/json" \
  -d '{"query": "{ teams { nodes { id name } } }"}' | python3 -m json.tool

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script is named and primarily behaves like a local action-item tracker, printing overdue and upcoming tasks from a JSON file. However, when --create-tickets is enabled it performs remote Jira issue creation and then rewrites the local actions file with ticket IDs, which is materially broader than simple tracking/reporting behavior.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
93% confidence
Finding

This code performs authenticated external transmission of meeting-derived content to Jira over the network. In the context of a TPM copilot, external transmission is expected, but it is still security-relevant because the payload may include sensitive internal project information and the destination URL is sourced from config/environment without additional validation.

Content

Scanner excerpt · scripts/action-tracker.sh (reported line 101)May include surrounding context.

sh
if a.get("jira_ticket"):
                continue
            try:
                resp = requests.post(
                    f"{jira_url}/rest/api/3/issue",
                    auth=auth,
                    headers={"Content-Type": "application/json"},

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script sends meeting-derived action item content, owners, due dates, and source file references to Jira as soon as --create-tickets is used, without a per-item preview or confirmation. Because meeting notes can contain sensitive project details, this can cause unintended disclosure to an external SaaS system or to the wrong Jira project if the action metadata is inaccurate or attacker-controlled.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The script automatically loads Jira and Slack credentials from config or environment variables and then performs outbound requests to Jira and optionally Slack without any explicit user-facing notice or confirmation at execution time. In a TPM/PM automation skill, this can unintentionally disclose project metadata such as ticket relationships, statuses, and program structure to external services or webhooks, especially if a webhook URL is misconfigured, attacker-controlled, or inherited from the environment.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
95% confidence
Finding

The Slack webhook POST sends dependency-risk data off host to an externally configured endpoint, and the endpoint is taken directly from config or environment without validation. Because the payload includes program name, ticket identifiers, statuses, and dependency relationships, a malicious or mistaken webhook configuration could exfiltrate sensitive operational data to an unauthorized recipient.

Content

Scanner excerpt · scripts/dependency-map.sh (reported line 151)May include surrounding context.

sh
for d in at_risk:
            text += f"• {d['from_ticket']} [{d.get('from_status','?')}] → blocks → {d['to_ticket']}\n"
        try:
            requests.post(webhook, json={"text": text}, timeout=10)
            print("\n   📤 Alert sent to Slack")
        except:
            pass

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
94% confidence
Finding

The script initializes a config file that explicitly contains fields for sensitive credentials such as Jira API tokens, GitHub tokens, Slack webhook URLs, and email API keys, and writes it to disk without setting restrictive permissions or warning the user about secret storage. In a TPM/PM automation skill that aggregates access to multiple business systems, this increases the chance that secrets will be stored insecurely, committed to source control, or exposed to other local users/processes.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The code reads sensitive credentials including JIRA_API_TOKEN and uses them for authenticated HTTP requests to Jira. Although the network access is part of generating agenda content, the file itself provides no user-facing disclosure, prompt, or explanatory comment warning that credentials will be used and project data fetched from an external service.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The script reads Jira and Slack credentials from configuration or environment variables and uses them for HTTP requests, but it provides no user-facing notice, confirmation, comment, or docstring disclosing that credentials and project metadata will be sent to external services. This matches the code-file warning criterion for sensitive environment access and network transmission lacking visible disclosure.

Content

No source excerpt is available for this finding.

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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 · scripts/risk-radar.sh (reported line 251)May include surrounding context.

sh
for r in new_risks:
            alert_text += f"• [{r['severity'].upper()}] {r['title']}\n"
        try:
            requests.post(webhook, json={"text": alert_text}, timeout=10)
            print(f"\n   📤 Alert sent to Slack")
        except:
            pass

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 · scripts/status-report.sh (reported line 179)May include surrounding context.

sh
}
            }
            """
            resp = requests.post(
                "https://api.linear.app/graphql",
                headers={"Authorization": linear_key, "Content-Type": "application/json"},
                json={"query": query, "variables": {"teamId": team_id}},

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% 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 · scripts/status-report.sh (reported line 179)May include surrounding context.

sh
}
            }
            """
            resp = requests.post(
                "https://api.linear.app/graphql",
                headers={"Authorization": linear_key, "Content-Type": "application/json"},
                json={"query": query, "variables": {"teamId": team_id}},

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · references/linear-setup.md (reported line 23)May include surrounding context.

md
}
            """
            resp = requests.post(
                "https://api.linear.app/graphql",
                headers={"Authorization": linear_key, "Content-Type": "application/json"},
                json={"query": query, "variables": {"teamId": team_id}},
                timeout=15

External Transmission

Medium
Category
Data Exfiltration
Confidence
60% 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 · scripts/status-report.sh (reported line 180)May include surrounding context.

sh
}
            """
            resp = requests.post(
                "https://api.linear.app/graphql",
                headers={"Authorization": linear_key, "Content-Type": "application/json"},
                json={"query": query, "variables": {"teamId": team_id}},
                timeout=15

Static analysis

No suspicious patterns detected.