Back to skill

Security audit

Azure DevOps

Security checks for vulnerabilities and agentic risk

Overview

The skill is coherent Azure DevOps helper documentation, but it tells agents to persist a personal access token in a regular config file and includes state-changing API actions without clear safety controls.

Install only if you are comfortable granting an agent Azure DevOps access. Use a least-privilege, short-lived PAT, prefer an environment variable or secret manager over persistent config storage, restrict any config file to owner-only access, and require an explicit review before creating pull requests or making other Azure DevOps changes.

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

T09 · Insecure Skill Coding Practices

Warning
Location
SKILL.md:18
Finding
Azure DevOps PAT Stored in Plaintext Configuration<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 18–35 **Vulnerability Type**: Plaintext storage of sensitive credentials **Risk Level**: Medium ### Vulnerable Code ```markdown **If values are missing from `~/.openclaw/openclaw.json`, the agent should:** 1. **ASK** the user for the missing PAT and/or organization name 2. Store them in `~/.openclaw/openclaw.json` under `skills.entries["azure-devops"]` ### Example Config ```json5 { skills: { entries: { "azure-devops": { apiKey: "YOUR_PERSONAL_ACCESS_TOKEN", // AZURE_DEVOPS_PAT env: { AZURE_DEVOPS_ORG: "YourOrganizationName" } } } } } ``` ``` ### Technical Analysis The Skill instructs the Agent to solicit an Azure DevOps Personal Access Token and persist it in `~/.openclaw/openclaw.json`. The instructions do not require encryption, integration with an operating-system credential store, restrictive file permissions, minimal token scopes, or token expiration. Although the Skill later states that the PAT must not be logged or exposed in responses, that safeguard does not protect the credential at rest. A regular configuration file may be accessible to other local users or processes, copied into backups, included in diagnostic archives, or disclosed through accidental configuration sharing. ### Attack Path 1. The Skill detects that the Azure DevOps PAT is absent from the configuration. 2. The Agent asks the user to provide a valid PAT. 3. The Agent writes the PAT into `~/.openclaw/openclaw.json` as the `apiKey` value. 4. A local process, user, backup system, diagnostic collector, or malicious program obtains read access to the configuration file. 5. The exposed token is extracted and submitted to the official Azure DevOps API. 6. The attacker performs operations permitted by the PAT until it expires or is revoked. ### Impact Assessment An attacker who obtains the PAT can act with all Azure DevOps privileges assigned to that to ...[truncated 428 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. Store the PAT in an operating-system credential manager or supported secret-management service instead of a regular JSON configuration file. 2. Prefer short-lived credentials or federated authentication where the environment supports them. 3. If file-based storage is unavoidable: - Store the secret separately from general configuration. - Require owner-only permissions such as `0600`. - Verify the file owner before reading or writing it. - Exclude the file from source control, backups, logs, and diagnostic bundles. 4. Request only the minimum Azure DevOps scopes necessary for the requested operation. 5. Document token expiration and rotation procedures. 6. Never echo the token in commands, logs, error messages, or Agent responses. 7. Avoid asking users to paste credentials into conversational content when a secure secret-entry mechanism is available. ]]>

T09 · Insecure Skill Coding Practices

Note
Location
SKILL.md:62
Finding
Unsafe JSON Construction in Pull Request Creation Command<![CDATA[ ## Vulnerability Details **File Location**: `SKILL.md`, lines 62–79 **Vulnerability Type**: Improper escaping of variable data in a JSON request body **Risk Level**: Low ### Vulnerable Code ```bash PROJECT="YourProject" REPO_ID="repo-id-here" SOURCE_BRANCH="feature/my-branch" TARGET_BRANCH="main" TITLE="PR Title" DESCRIPTION="PR Description" curl -s -u ":${AZURE_DEVOPS_PAT}" \ -H "Content-Type: application/json" \ -X POST \ -d '{ "sourceRefName": "refs/heads/'"${SOURCE_BRANCH}"'", "targetRefName": "refs/heads/'"${TARGET_BRANCH}"'", "title": "'"${TITLE}"'", "description": "'"${DESCRIPTION}"'" }' \ "https://dev.azure.com/${AZURE_DEVOPS_ORG}/${PROJECT}/_apis/git/repositories/${REPO_ID}/pullrequests?api-version=7.1" ``` ### Technical Analysis The command constructs JSON by directly interpolating shell variables into a quoted string. Values such as `TITLE`, `DESCRIPTION`, `SOURCE_BRANCH`, and `TARGET_BRANCH` are not JSON-escaped before insertion. A value containing double quotes, backslashes, newlines, or other JSON control characters can invalidate the request or alter its structure. A crafted value may terminate its intended JSON string and inject additional JSON properties into the authenticated request. This pattern is not direct shell command injection because shell metacharacters produced by parameter expansion are not reparsed as shell syntax. The primary issue is JSON injection and request-integrity failure. ### Attack Path 1. An untrusted source supplies or influences a pull-request title, description, or branch name. 2. The supplied value contains JSON syntax, such as an embedded quote followed by additional properties. 3. The Agent assigns the value to one of the documented shell variables. 4. Direct interpolation inserts the unescaped content into the JSON request body. 5. The resulting body is either rejected, causing a denial of the requested operation, or remains valid with attacker-influenced fields. 6. `c ...[truncated 743 chars]
Remediation
<![CDATA[ ## Remediation Suggestions Construct the request body with a JSON-aware serializer such as `jq` rather than manual string interpolation: ```bash jq -n \ --arg source "refs/heads/$SOURCE_BRANCH" \ --arg target "refs/heads/$TARGET_BRANCH" \ --arg title "$TITLE" \ --arg description "$DESCRIPTION" \ '{ sourceRefName: $source, targetRefName: $target, title: $title, description: $description }' | curl -sS -u ":${AZURE_DEVOPS_PAT}" \ -H "Content-Type: application/json" \ -X POST \ --data-binary @- \ "https://dev.azure.com/${AZURE_DEVOPS_ORG}/${PROJECT}/_apis/git/repositories/${REPO_ID}/pullrequests?api-version=7.1" ``` Additionally: 1. Validate project, repository, and branch identifiers against expected Azure DevOps formats. 2. Treat titles and descriptions obtained from external content as untrusted. 3. Use `curl -sS --fail-with-body` so HTTP and transport failures are detected. 4. Check the generated JSON and require confirmation before performing a state-changing request when values originate from an untrusted source. ]]>
Vulnerability Patterns
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Rogue AgentSelf-Modification, Session Persistence
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (5)

Credential Access

High
Category
Privilege Escalation
Content
## Check before running for valid Configuration, if values missing ask the user!

**Required:**
- `AZURE_DEVOPS_PAT`: Personal Access Token
- `AZURE_DEVOPS_ORG`: Organization name

**If values are missing from `~/.openclaw/openclaw.json`, the agent should:**
Confidence
70% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Session Persistence

Medium
Category
Rogue Agent
Content
---
name: azure-devops
description: List Azure DevOps projects, repositories, and branches; create pull requests; manage work items; check build status. Use when working with Azure DevOps resources, checking PR status, querying project structure, or automating DevOps workflows.
metadata: {"openclaw": {"emoji": "☁️", "requires": {"bins": ["curl", "jq"], "env": ["AZURE_DEVOPS_PAT"]}, "primaryEnv": "AZURE_DEVOPS_PAT"}}
---
Confidence
60% 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.

Missing User Warnings

Medium
Confidence
94% confidence
Finding
The skill explicitly instructs the agent to ask the user for a Personal Access Token and persist it in a local config file, but provides no warning about secure storage, file permissions, lifetime, or safer alternatives. Storing long-lived credentials on disk increases the chance of token theft through local compromise, backups, logs, or accidental file exposure.

External Transmission

Medium
Category
Data Exfiltration
Content
TITLE="PR Title"
DESCRIPTION="PR Description"

curl -s -u ":${AZURE_DEVOPS_PAT}" \
  -H "Content-Type: application/json" \
  -X POST \
  -d '{
Confidence
60% confidence
Finding
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.

Missing User Warnings

Low
Confidence
85% confidence
Finding
The pull request example performs a state-changing POST to Azure DevOps but is presented alongside read-only queries without a prominent warning or confirmation requirement. In an agent setting, this can lead to unintended remote modifications if the action is executed automatically or with ambiguous user intent.

Static analysis

No suspicious patterns detected.