Back to skill

Security audit

OpenProject by altf1be

Security checks for vulnerabilities and agentic risk

Overview

This is a real OpenProject management skill, but it needs review because it uses a powerful API token, exposes broad data-changing commands, and can send the token to an HTTP host if configured that way.

Install only if you trust this publisher and are comfortable giving the skill an OpenProject API token with the same permissions you would give an automation user. Use HTTPS-only OP_HOST values, prefer a dedicated least-privilege OpenProject account/token, avoid admin tokens unless needed, and review bulk notification, custom action, group/user, news, and attachment-upload commands before letting an agent run them.

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
scripts/openproject.mjs:75
Finding
OpenProject API Token May Be Transmitted over Plaintext HTTP## Vulnerability Details **File Location**: `scripts/openproject.mjs`, lines 75–100 **Vulnerability Type**: Plaintext transmission of credentials **Risk Level**: High ### Vulnerable Code ```js function authHeader() { // OpenProject uses Basic auth with 'apikey' as username const token = Buffer.from(`apikey:${CFG.apiToken}`).toString('base64'); return `Basic ${token}`; } function baseUrl() { const host = CFG.host.replace(/\/+$/, ''); const prefix = host.startsWith('http') ? host : `https://${host}`; return `${prefix}/api/v3`; } async function opFetch(path, options = {}, retries = 3) { const url = path.startsWith('http') ? path : `${baseUrl()}${path}`; const headers = { 'Authorization': authHeader(), 'Accept': 'application/json', ...options.headers, }; if (!(options.body instanceof FormData)) { headers['Content-Type'] = headers['Content-Type'] || 'application/json'; } for (let attempt = 1; attempt <= retries; attempt++) { const resp = await fetch(url, { ...options, headers }); ``` ### Technical Analysis The `baseUrl()` function accepts an `OP_HOST` value beginning with either `http://` or `https://` because it preserves any value that starts with `http`. Every API request then includes the OpenProject API token in an HTTP Basic `Authorization` header. Base64 encoding does not provide confidentiality. If `OP_HOST` specifies a plaintext HTTP endpoint, the API token, request payloads, and API responses are transmitted without transport encryption. This is especially relevant because the project explicitly supports self-hosted instances, where users may configure an internal HTTP endpoint. The same transport issue also affects the direct attachment-upload requests because they use `baseUrl()` and attach the same authorization header. ### Attack Path 1. A user or agent configures a self-hosted instance using an address such as `OP_HOST=http://openproject.internal`. 2. The CLI preserves the plaintext HTTP scheme w ...[truncated 1598 chars]
Remediation
## Remediation Suggestions 1. Parse `OP_HOST` using the standard URL parser and reject every protocol except HTTPS: ```js function baseUrl() { let url; try { url = new URL(CFG.host.includes('://') ? CFG.host : `https://${CFG.host}`); } catch { throw new Error('OP_HOST must be a valid OpenProject URL'); } if (url.protocol !== 'https:') { throw new Error('OP_HOST must use HTTPS to protect the API token'); } url.pathname = `${url.pathname.replace(/\/+$/, '')}/api/v3`; return url.toString().replace(/\/$/, ''); } ``` 2. If plaintext HTTP is required for local development, require an explicit option such as `OP_ALLOW_INSECURE_HTTP=true` and permit it only for loopback addresses such as `127.0.0.1`, `::1`, or `localhost`. Emit a prominent warning when this mode is active. 3. Do not treat arbitrary strings beginning with `http` as valid URLs. Validate the scheme, hostname, optional port, username, password, and path explicitly. 4. Ensure all request paths remain relative to the validated OpenProject origin. Avoid accepting absolute URLs in `opFetch()` unless they are separately validated as same-origin, preventing future callers from accidentally forwarding the authorization header to another host. 5. Apply the same validated HTTPS origin to all direct upload requests for work-package, meeting, and wiki attachments. 6. Update `README.md` and `SKILL.md` to state that remote and production OpenProject instances must use HTTPS with valid certificate verification. 7. After deployment of the fix, revoke and replace any API token that may previously have been used with an HTTP endpoint.
Vulnerability Patterns
  • 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
  • Rogue AgentSelf-Modification, Session Persistence
  • MCP Least PrivilegeUnderdeclared Capability, Wildcard Permission, Missing Permission Declaration
Findings (17)

Credential Access

High
Category
Privilege Escalation
Content
npm install

# 3. Configure
cp .env.example .env
# Edit .env with your OpenProject URL and API token

# 4. Use
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Setup

1. Log in to your OpenProject instance
2. Go to **My Account → Access Tokens → + Add**
3. Create an API token and copy it
4. Set environment variables (or create `.env` in `{baseDir}`):
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Setup

1. Log in to your OpenProject instance
2. Go to **My Account → Access Tokens → + Add**
3. Create an API token and copy it
4. Set environment variables (or create `.env` in `{baseDir}`):
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
// ── Config ──────────────────────────────────────────────────────────────────

config(); // load .env

let _cfg;
function getCfg() {
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
// ── Config ──────────────────────────────────────────────────────────────────

config(); // load .env

let _cfg;
function getCfg() {
Confidence
60% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Lp3

Medium
Category
MCP Least Privilege
Confidence
93% confidence
Finding
The skill requires environment variables and makes network calls, but it does not declare an explicit tool scope such as allowed-tools or permissions. This creates an avoidable trust gap: a host system or reviewer cannot easily constrain or audit the skill's runtime capabilities, increasing the risk of unintended data access or outbound requests if the skill is invoked in a broader agent environment.

Session Persistence

Medium
Category
Rogue Agent
Content
// ── Work Package commands ───────────────────────────────────────────────────

async function cmdWpList(options) {
  const project = options.project || CFG.defaultProject;
  const filters = [];
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
// ── Work Package commands ───────────────────────────────────────────────────

async function cmdWpList(options) {
  const project = options.project || CFG.defaultProject;
  const filters = [];
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
// ── Work Package commands ───────────────────────────────────────────────────

async function cmdWpList(options) {
  const project = options.project || CFG.defaultProject;
  const filters = [];
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Session Persistence

Medium
Category
Rogue Agent
Content
// ── Work Package commands ───────────────────────────────────────────────────

async function cmdWpList(options) {
  const project = options.project || CFG.defaultProject;
  const filters = [];
Confidence
75% confidence
Finding
Skill establishes unauthorized persistence across sessions via cron jobs, startup scripts, or state files. Session persistence allows an attacker to maintain access beyond the current interaction.

Description-Behavior Mismatch

Medium
Confidence
94% confidence
Finding
The skill’s declared purpose is general OpenProject CRUD for common project-management resources, but the CLI also exposes instance-level and administrative capabilities such as configuration inspection, OAuth metadata access, capabilities enumeration, and user/group administration. This scope expansion increases the attack surface and can enable unintended privilege use, sensitive metadata disclosure, or policy-violating actions when an agent is only expected to manage ordinary project records.

Context-Inappropriate Capability

Medium
Confidence
95% confidence
Finding
The code exposes commands to read OAuth application and client credential metadata, including client IDs, redirect URIs, and confidentiality flags. Even without secrets, this information is sensitive infrastructure metadata that can aid reconnaissance, facilitate phishing or OAuth abuse planning, and exceeds the expected scope of a project-management CRUD skill.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The bulk mark-read operation can modify all matching notifications without any confirmation prompt, dry-run preview, or count threshold. In an agent context, a mistaken or manipulated invocation can silently alter large numbers of user-visible records and erase important unread-state signals.

Missing User Warnings

Medium
Confidence
90% confidence
Finding
The bulk mark-unread operation similarly changes all matching notifications with no confirmation or safety interlock. This can create noise, confuse workflows, and be abused by a prompt-injected agent to manipulate user attention at scale.

Unpinned Dependencies

Low
Category
Supply Chain
Content
"url": "https://github.com/ALT-F1-OpenClaw/openclaw-skill-openproject.git"
  },
  "dependencies": {
    "commander": "^12.0.0",
    "dotenv": "^16.0.0"
  },
  "engines": {
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Unpinned Dependencies

Low
Category
Supply Chain
Content
},
  "dependencies": {
    "commander": "^12.0.0",
    "dotenv": "^16.0.0"
  },
  "engines": {
    "node": ">=18.0.0"
Confidence
40% confidence
Finding
Dependencies lack version pinning, allowing potential malicious package updates. Consider pinning versions.

Intent-Code Divergence

Low
Confidence
90% confidence
Finding
The comment claims there is no arbitrary file access, but attachment commands will read any local path supplied by the caller as long as it does not contain '..' after normalization. Because absolute paths and many sensitive files remain readable, an agent or caller can exfiltrate locally accessible data by uploading it to the remote OpenProject instance.

Static analysis

Detected: suspicious.env_credential_access

Environment variable access combined with network send.

Critical
Code
suspicious.env_credential_access
Location
scripts/openproject.mjs:28