Back to skill

Security audit

Prometheus

Security checks for vulnerabilities and agentic risk

Overview

The skill is a coherent Prometheus query tool, but its credential handling is broad enough that users should review it before installing.

Install only if you are comfortable with this skill reading local .env files, storing Prometheus credentials in plaintext config, and using environment Basic Auth credentials as fallbacks for configured instances. Prefer per-instance credentials, restrict config file permissions, avoid running it from untrusted project directories, and do not use --all with mixed-trust Prometheus endpoints until credential scoping is fixed.

Vulnerability Patterns
  • Unauthorized Access and Privilege EscalationObtains permissions beyond the task's legitimate needs
  • 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 (3)

T09 · Insecure Skill Coding Practices

Error
Location
scripts/common.js:160
Finding
Global Basic Auth credentials may be disclosed to unrelated Prometheus instances<![CDATA[ ## Vulnerability Details **File Location**: `scripts/common.js:160-169` **Related Request Sites**: `scripts/query.js:27-29`, `55-57`, `86-88`, `108-110`, `140-142`, `166-168`, `196-198`, `224-226`, `249-251`, `274-276`, `300-302`, `331-333`, and `362-364` **Vulnerability Type**: Cross-instance credential disclosure caused by unsafe environment-variable fallback **Risk Level**: High ### Vulnerable Code ```js export function createAuthHeader(instance = null) { const headers = { 'Accept': 'application/json' }; const user = instance?.user || process.env.PROMETHEUS_USER; const password = instance?.password || process.env.PROMETHEUS_PASSWORD; if (user && password) { const auth = Buffer.from(`${user}:${password}`).toString('base64'); headers['Authorization'] = `Basic ${auth}`; } return headers; } ``` Every outgoing request passes its selected instance to this function, for example: ```js const response = await fetch(`${url}?${params}`, { headers: createAuthHeader(targetInstance) }); ``` ### Technical Analysis When a configured Prometheus instance does not have its own `user` or `password`, `createAuthHeader()` silently substitutes the global `PROMETHEUS_USER` and `PROMETHEUS_PASSWORD` values. This behavior crosses instance trust boundaries. In a multi-instance deployment, global credentials intended for a legacy or trusted Prometheus endpoint can consequently be attached to requests sent to another configured endpoint. That other endpoint may be operated by a different party, compromised, or deliberately configured by an attacker. HTTP Basic Auth only Base64-encodes credentials; it does not encrypt them. If the destination URL uses plain HTTP, a network observer may also recover the credentials in transit. ### Attack Path 1. The victim environment or loaded `.env` file defines `PROMETHEUS_USER` and `PROMETHEUS_PASSWORD`. 2. An attacker adds, modifies, or convinces the user to configure a Prometheus instance who ...[truncated 1180 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not use global credentials when an explicit instance object is supplied: ```js export function createAuthHeader(instance) { const headers = { Accept: 'application/json' }; const user = instance?.user; const password = instance?.password; if (user && password) { const auth = Buffer.from(`${user}:${password}`).toString('base64'); headers.Authorization = `Basic ${auth}`; } return headers; } ``` 2. Preserve legacy environment-variable support only by converting those variables into the single fallback instance inside `loadConfig()`. Do not make them implicit credentials for file-configured instances. 3. Validate that credentials are either both present or both absent for each instance. 4. Require HTTPS for authenticated remote instances. If plain HTTP must be supported for localhost or isolated networks, require an explicit insecure-transport opt-in and display a warning. 5. Document credential scoping clearly and add tests confirming that an unauthenticated instance never receives environment credentials. 6. Consider supporting per-instance environment-variable references or a secret store instead of embedding credentials directly in configuration. ]]>

T09 · Insecure Skill Coding Practices

Warning
Location
scripts/cli.js:126
Finding
The configuration wizard stores passwords in plaintext without enforcing restrictive file permissions<![CDATA[ ## Vulnerability Details **File Location**: `scripts/cli.js:126-138` and `scripts/cli.js:164-173` **Vulnerability Type**: Insecure local credential storage **Risk Level**: Medium ### Vulnerable Code The wizard collects the password and inserts it directly into the configuration object: ```js if (auth.toLowerCase() === 'y') { user = await prompt(rl, 'Username'); password = await prompt(rl, 'Password'); } instances.push({ name, url: url.replace(/\/$/, ''), // Remove trailing slash ...(user && { user }), ...(password && { password }) }); ``` It then writes that object as plaintext JSON without an explicit secure mode: ```js try { // Ensure directory exists const configDir = dirname(configFile); if (!existsSync(configDir)) { mkdirSync(configDir, { recursive: true }); } writeFileSync(configFile, JSON.stringify(config, null, 2)); console.log(`\n✅ Configuration saved to ${configFile}`); ``` ### Technical Analysis The setup wizard persists Basic Auth passwords directly in a JSON file. The `writeFileSync()` call does not specify a restrictive file mode, so the resulting access permissions depend on the process umask and the permissions of any pre-existing file. If the configuration file already exists with broad permissions, overwriting it does not necessarily correct those permissions. On a multi-user system or in a workspace accessible to other processes, this can expose the credentials to unrelated local principals. The password prompt also uses the normal `readline` interface, so password characters are visible while being entered. Although the primary vulnerability is insecure persistence, visible input increases the chance of shoulder-surfing or terminal capture. ### Attack Path 1. A user runs `node scripts/cli.js init`. 2. The user enables HTTP Basic Auth and enters a Prometheus username and password. 3. The wizard stores those values directly in the `instances` array. 4. The complete configuration is written as ...[truncated 832 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Create the configuration file with owner-only permissions: ```js writeFileSync(configFile, JSON.stringify(config, null, 2), { encoding: 'utf8', mode: 0o600 }); ``` 2. Explicitly correct the permissions of existing files after writing, because the creation mode alone may not change an existing file: ```js chmodSync(configFile, 0o600); ``` 3. Ensure newly created configuration directories are not broadly writable. 4. Prefer storing passwords in an operating-system credential manager, dedicated secret store, or protected environment variable. 5. If file-based secrets remain supported, warn users clearly that credentials will be stored in plaintext. 6. Replace the visible password prompt with a non-echoing secret-input implementation. 7. Refuse to use a credential-bearing configuration file if it is group-readable or world-readable, or at minimum emit a prominent warning. ]]>

T05 · Unauthorized Access and Privilege Escalation

Warning
Location
scripts/common.js:36
Finding
The CLI imports all variables from workspace and current-directory .env files<![CDATA[ ## Vulnerability Details **File Location**: `scripts/common.js:36-67` **Vulnerability Type**: Excessive local file access and unsafe environment mutation **Risk Level**: Medium ### Vulnerable Code ```js function loadEnvFile() { if (envLoaded) return; const workspaceDir = getWorkspaceDir(); const envPaths = [ join(workspaceDir, '.env'), join(process.cwd(), '.env'), ]; for (const envPath of envPaths) { if (existsSync(envPath)) { try { const content = readFileSync(envPath, 'utf8'); const lines = content.split('\n'); for (const line of lines) { const trimmed = line.trim(); // Skip comments and empty lines if (!trimmed || trimmed.startsWith('#')) continue; const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/); if (match) { const [, key, value] = match; // Only set if not already in env if (!process.env[key]) { // Remove quotes if present const cleanValue = value.replace(/^["']|["']$/g, ''); process.env[key] = cleanValue; } } } } catch (err) { // Silently ignore .env read errors } } } envLoaded = true; } ``` ### Technical Analysis The Skill only documents a need for `PROMETHEUS_URL`, `PROMETHEUS_USER`, and `PROMETHEUS_PASSWORD`, but it reads the entire contents of both the workspace `.env` file and the invocation directory's `.env` file. It then imports every syntactically valid variable into `process.env`. This behavior violates least privilege because unrelated credentials and application secrets are read even though they are unnecessary for Prometheus operations. Reading `process.cwd()/.env` also makes behavior dependent on the directory from which the CLI is launched, allowing a locally controlled project directory to influence authentication and endpoint selection. The broad i ...[truncated 2056 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Do not import arbitrary `.env` keys. Parse only the documented Prometheus variables: ```js const allowedKeys = new Set([ 'PROMETHEUS_URL', 'PROMETHEUS_USER', 'PROMETHEUS_PASSWORD', 'PROMETHEUS_CONFIG' ]); if (match) { const [, key, value] = match; if (allowedKeys.has(key) && process.env[key] === undefined) { process.env[key] = value.replace(/^["']|["']$/g, ''); } } ``` 2. Remove automatic loading of `process.cwd()/.env`, or require an explicit command-line option to select an environment file. 3. Use one documented, trusted environment-file location rather than searching multiple implicit locations. 4. Avoid mutating `process.env`; return a dedicated Prometheus configuration object instead. 5. Report environment-file parsing and permission errors rather than silently ignoring them. 6. Validate URLs and credential combinations before making requests. 7. Add tests showing that unrelated `.env` variables are never read into process state and that changing the current working directory cannot silently redirect requests. ]]>
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
  • Excessive AgencyUnrestricted Tool Access, Autonomous Decision Making, Scope Creep
Findings (31)

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Ae1

High
Category
analysis-evasion
Content
node scripts/cli.js init
Confidence
100% confidence
Finding
Referenced artifact was not completely inspected

Credential Access

High
Category
Privilege Escalation
Content
}

/**
 * Load environment variables from .env file
 */
function loadEnvFile() {
  if (envLoaded) return;
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
}

/**
 * Load environment variables from .env file
 */
function loadEnvFile() {
  if (envLoaded) return;
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
}

/**
 * Load environment variables from .env file
 */
function loadEnvFile() {
  if (envLoaded) return;
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
const workspaceDir = getWorkspaceDir();
  const envPaths = [
    join(workspaceDir, '.env'),
    join(process.cwd(), '.env'),
  ];
Confidence
84% confidence
Finding
The code automatically loads a .env file from the workspace root, which may contain secrets, without any trust boundary checks or user opt-in. In an agent/skill context, workspace contents can be influenced by external tasks or repositories, so implicit secret loading can cause credential confusion and unintended use of attacker-supplied Prometheus endpoints or credentials.

Credential Access

High
Category
Privilege Escalation
Content
const workspaceDir = getWorkspaceDir();
  const envPaths = [
    join(workspaceDir, '.env'),
    join(process.cwd(), '.env'),
  ];
  
  for (const envPath of envPaths) {
Confidence
91% confidence
Finding
Loading .env from process.cwd() is especially risky because the current working directory is often caller-controlled in CLI/agent environments. An attacker who can place a .env in the execution directory can inject PROMETHEUS_URL, PROMETHEUS_USER, or PROMETHEUS_PASSWORD values, redirecting requests and causing credential disclosure via Basic Auth to an attacker-controlled server.

Static analysis

No suspicious patterns detected.