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

Content
View full analysis

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
View 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(/\/$/, '');
}
  1. 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.

  2. Do not treat arbitrary strings beginning with http as valid URLs. Validate the scheme, hostname, optional port, username, password, and path explicitly.

  3. 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.

  4. Apply the same validated HTTPS origin to all direct upload requests for work-package, meeting, and wiki attachments.

  5. Update README.md and SKILL.md to state that remote and production OpenProject instances must use HTTPS with valid certificate verification.

  6. 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
Confidence
60% confidence
Finding

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

Content

Scanner excerpt · README.md (reported line 78)May include surrounding context.

md
npm install

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

# 4. Use

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

Scanner excerpt · README.md (reported line 90)May include surrounding context.

md
## 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}`):

Credential Access

High
Category
Privilege Escalation
Confidence
70% confidence
Finding

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

Content

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

md
## 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}`):

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 · README.md (reported line 79)May include surrounding context.

md
// ── Config ──────────────────────────────────────────────────────────────────

config(); // load .env

let _cfg;
function getCfg() {

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 · scripts/openproject.mjs (reported line 22)May include surrounding context.

js
// ── Config ──────────────────────────────────────────────────────────────────

config(); // load .env

let _cfg;
function getCfg() {

Undeclared Tool Scope

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.

Content

No source excerpt is available for this finding.

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · scripts/openproject.mjs (reported line 148)May include surrounding context.

js
// ── Work Package commands ───────────────────────────────────────────────────

async function cmdWpList(options) {
  const project = options.project || CFG.defaultProject;
  const filters = [];

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · scripts/openproject.mjs (reported line 1621)May include surrounding context.

js
// ── Work Package commands ───────────────────────────────────────────────────

async function cmdWpList(options) {
  const project = options.project || CFG.defaultProject;
  const filters = [];

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · scripts/openproject.mjs (reported line 2308)May include surrounding context.

js
// ── Work Package commands ───────────────────────────────────────────────────

async function cmdWpList(options) {
  const project = options.project || CFG.defaultProject;
  const filters = [];

Session Persistence

Medium
Category
Rogue Agent
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.

Content

Scanner excerpt · scripts/openproject.mjs (reported line 2690)May include surrounding context.

js
// ── Work Package commands ───────────────────────────────────────────────────

async function cmdWpList(options) {
  const project = options.project || CFG.defaultProject;
  const filters = [];

Description-Behavior Mismatch

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

Content

No source excerpt is available for this finding.

Context-Inappropriate Capability

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

Content

No source excerpt is available for this finding.

Missing User Warnings

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

Content

No source excerpt is available for this finding.

Missing User Warnings

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

Content

No source excerpt is available for this finding.

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 29)May include surrounding context.

json
"url": "https://github.com/ALT-F1-OpenClaw/openclaw-skill-openproject.git"
  },
  "dependencies": {
    "commander": "^12.0.0",
    "dotenv": "^16.0.0"
  },
  "engines": {

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 30)May include surrounding context.

json
},
  "dependencies": {
    "commander": "^12.0.0",
    "dotenv": "^16.0.0"
  },
  "engines": {
    "node": ">=18.0.0"

Intent-Code Divergence

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

Content

No source excerpt is available for this finding.