Back to skill

Security audit

honor

Security checks for vulnerabilities and agentic risk

Overview

The skill mostly matches a Picqer dashboard purpose, but its Picqer tenant setting can redirect the API key to an unintended host, so it should be reviewed before installation.

Review before installing. Use only a tightly scoped Picqer API key, validate or hardcode the exact Picqer tenant hostname before use, and do not run the included cron.ts auto-refresh file unless you intentionally want recurring authenticated syncs. Pin dependencies and add a lockfile for a more controlled install.

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

T09 · Insecure Skill Coding Practices

Error
Location
tools/picqer-api.ts:5
Finding

Unvalidated Picqer Subdomain Can Redirect API Credentials to an Attacker-Controlled Host

Content
View full analysis

Vulnerability Details

File Location: tools/picqer-api.ts, lines 5–20
Vulnerability Type: Credential disclosure through unvalidated URL construction
Risk Level: High

Vulnerable Code

ts
const { subdomain, apiKey } = getPicqerConfig();
const url = new URL(`https://${subdomain}.picqer.com/api/v1${path}`);

if (searchParams) {
  Object.entries(searchParams).forEach(([k, v]) => {
    if (v) url.searchParams.set(k, v);
  });
}

const res = await fetch(url.toString(), {
  headers: {
    'Authorization': `Basic ${Buffer.from(`${apiKey}:`).toString('base64')}`,
    'User-Agent': 'FutureFulfillment-Dashboard (internal)'
  }
});

if (!res.ok) {

The environment-controlled value originates in env.ts, lines 4–10:

ts
const subdomain = process.env.PICQER_SUBDOMAIN;
const apiKey = process.env.PICQER_API_KEY;

if (!subdomain || !apiKey) {
  throw new Error('Picqer API not configured. Set PICQER_SUBDOMAIN and PICQER_API_KEY in .env');
}

return { subdomain, apiKey };

Technical Analysis

PICQER_SUBDOMAIN is interpolated directly into an absolute URL without validating that it is a single DNS label. URL delimiters such as / can therefore change which portion of the generated string is interpreted as the hostname.

For example, setting the subdomain to attacker.example/x produces:

text
https://attacker.example/x.picqer.com/api/v1/picklists

The effective hostname is attacker.example, not a host beneath picqer.com. The application subsequently attaches the Picqer API key as a Basic Authorization header and sends the request to that effective hostname.

Base64 encoding does not protect the credential; it is only an encoding of the API key followed by a colon. TLS protects the request in transit but intentionally delivers it to the attacker-controlled HTTPS endpoint selected by the manipulated URL.

Attack Path

  1. An attacker gains the ...[truncated 1616 chars]
Remediation
View remediation

Remediation Suggestions

  1. Validate PICQER_SUBDOMAIN as exactly one DNS label before using it:

    ts
    const SUBDOMAIN_PATTERN =
      /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/i;
    
    if (!SUBDOMAIN_PATTERN.test(subdomain)) {
      throw new Error('Invalid Picqer subdomain');
    }
    
  2. Construct and verify the expected hostname separately:

    ts
    const hostname = `${subdomain}.picqer.com`;
    const url = new URL(`/api/v1${path}`, `https://${hostname}`);
    
    if (
      url.protocol !== 'https:' ||
      url.hostname !== hostname ||
      url.port !== ''
    ) {
      throw new Error('Invalid Picqer API URL');
    }
    
  3. Prefer an allowlist containing the exact permitted Picqer tenant hostname when the deployment uses a single known tenant.

  4. Validate path as an internal API path and reject absolute URLs, backslashes, control characters, and unexpected traversal sequences before URL construction.

  5. Keep outbound network controls in place so the process can connect only to the approved Picqer hostname where practical.

  6. If malicious configuration may already have been used, revoke and rotate PICQER_API_KEY, inspect outbound request logs for unexpected hosts, and review Picqer audit logs for unauthorized API activity.

Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (7)

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

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

md
## Security

- API key only in local .env file
- No credentials in OpenClaw config
- Access via Tailscale only

Credential Access

High
Category
Privilege Escalation
Confidence
60% confidence
Finding

Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Content

Scanner excerpt · env.ts (reported line 8)May include surrounding context.

ts
const apiKey = process.env.PICQER_API_KEY;
  
  if (!subdomain || !apiKey) {
    throw new Error('Picqer API not configured. Set PICQER_SUBDOMAIN and PICQER_API_KEY in .env');
  }
  
  return { subdomain, apiKey };

Missing User Warnings

Medium
Category
Not specified by scanner
Confidence
90% confidence
Finding

The code reads an API key from configuration and sends authenticated HTTP requests to an external Picqer endpoint. Within this file there is no confirmation prompt, user-facing log/print, or explanatory comment/docstring disclosing that user or system data may be transmitted to a third-party service.

Content

No source excerpt is available for this finding.

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
92% confidence
Finding

The dependency uses a caret range, which permits automatic installation of newer minor and patch versions. This increases supply-chain risk because a compromised or breaking upstream release could be pulled in without explicit review, especially significant here because @openclaw/sdk appears central to the skill's runtime behavior.

Content

Scanner excerpt · package.json (reported line 12)May include surrounding context.

json
"test": "node --experimental-specifier-resolution=node dist/test.js"
  },
  "dependencies": {
    "@openclaw/sdk": "^1.0.0",
    "dotenv": "^16.3.1"
  },
  "devDependencies": {

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
90% confidence
Finding

The dotenv dependency is specified with a caret range, allowing unreviewed minor/patch updates during installation. While dotenv is common and lower risk than a privileged SDK, this still creates a supply-chain exposure if an upstream release is malicious or vulnerable.

Content

Scanner excerpt · package.json (reported line 13)May include surrounding context.

json
},
  "dependencies": {
    "@openclaw/sdk": "^1.0.0",
    "dotenv": "^16.3.1"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 16)May include surrounding context.

json
"dotenv": "^16.3.1"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.0.0"
  }
}

Unpinned Dependencies

Low
Category
Supply Chain
Confidence
40% confidence
Finding

Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Content

Scanner excerpt · package.json (reported line 17)May include surrounding context.

json
},
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.0.0"
  }
}

Static analysis

No suspicious patterns detected.