Back to skill

Security audit

Jira API (REST + Agile)

Security checks for vulnerabilities and agentic risk

Overview

This Jira automation skill is mostly coherent, but its generic request command can send Jira credentials to non-Jira URLs if misused or influenced by untrusted instructions.

Review before installing. Use a narrowly scoped Jira token if possible, confirm all destructive Jira changes, and avoid the generic request command unless the path is a Jira-relative path such as /rest/api/3/myself. The helper should be fixed to reject absolute URLs, validate the final Jira origin, and constrain --data-file before broad agent-driven use.

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/jira_api.py:187
Finding
Jira API Token Disclosure Through Arbitrary Generic Request Destinations<![CDATA[ ## Vulnerability Details **File Location**: `scripts/jira_api.py`, lines 117–119, 187–200, and 636–646 **Vulnerability Type**: Arbitrary-host credential disclosure caused by insufficient request destination validation **Risk Level**: High ### Vulnerable Code Credential construction at lines 117–119: ```python def _basic_auth_header(user: str, token: str) -> str: raw = f"{user}:{token}".encode("utf-8") return "Basic " + base64.b64encode(raw).decode("ascii") ``` Generic request handling at lines 187–200: ```python def cmd_request(args, cfg): server = cfg["server"] url = urllib.parse.urljoin(server + "/", args.path.lstrip("/")) if args.query: q = urllib.parse.urlencode(args.query) url += ("&" if "?" in url else "?") + q body = None if args.data_json: body = json.loads(args.data_json) elif args.data_file: with open(args.data_file, "r", encoding="utf-8") as f: body = json.load(f) status, j, raw = _http(args.method, url, args.headers, body) ``` Credential loading and header assignment at lines 636–646: ```python cfg = _read_jira_config(args.jira_config) host = urllib.parse.urlparse(cfg["server"]).hostname if not host: raise RuntimeError("Could not parse Jira hostname") user, token = _netrc_auth_for_host(host, args.netrc) args.headers = { "Accept": "application/json", "Authorization": _basic_auth_header(user, token), "User-Agent": "openclaw-skill/jira-api", } ``` ### Technical Analysis The script legitimately reads a Jira API token from `.netrc` and encodes the username and token using Base64 for HTTP Basic authentication. Base64 encoding is required by the authentication protocol and is not, by itself, evidence of covert exfiltration. The vulnerability occurs because the generic `request` command does not require `args.path` to be a relative Jira API path. It passes the value to `urllib.parse.urljoin()`: ```python url = urllib.parse.urljoin(server + ...[truncated 3103 chars]
Remediation
<![CDATA[ ## Remediation Suggestions 1. **Reject absolute request targets.** Require `args.path` to be a relative Jira API path. Reject values containing a URL scheme, hostname, user information, or network-path reference. 2. **Validate the final origin.** After constructing the URL, compare its scheme, normalized hostname, and effective port with the configured Jira origin. Require HTTPS. ```python base = urllib.parse.urlsplit(cfg["server"]) candidate = urllib.parse.urlsplit(args.path) if candidate.scheme or candidate.netloc: raise ValueError("Absolute URLs are not allowed") target = urllib.parse.urljoin(cfg["server"].rstrip("/") + "/", args.path.lstrip("/")) parsed_target = urllib.parse.urlsplit(target) base_port = base.port or (443 if base.scheme == "https" else 80) target_port = parsed_target.port or (443 if parsed_target.scheme == "https" else 80) if ( base.scheme != "https" or parsed_target.scheme != "https" or parsed_target.hostname != base.hostname or target_port != base_port ): raise ValueError("Request target must remain on the configured Jira HTTPS origin") ``` 3. **Protect credentials across redirects.** Disable automatic redirects for authenticated requests or validate every redirect destination before forwarding the `Authorization` header. Credentials must never be forwarded to a different origin. 4. **Separate authenticated and unauthenticated HTTP logic.** Add the Jira authorization header only after the destination has passed origin validation. Avoid placing credentials in a globally reused header dictionary. 5. **Constrain local file input.** If `--data-file` is needed for Agent-driven use, resolve the path and restrict it to an explicitly approved workspace directory. Reject symlinks and paths escaping that directory. Alternatively, remove this option when it is not essential. 6. **Apply Jira-side least privilege.** Use a dedicated Jira service account and a narrowly scoped API token where supported. Limit projec ...[truncated 522 chars]
Vulnerability Patterns
  • Privilege EscalationExcessive Permissions, Sudo/Root Execution, Credential Access
  • Tool MisuseTool Parameter Abuse, Chaining Abuse, Unsafe Defaults
  • Prompt InjectionInstruction Override, Hidden Instructions, Exfiltration Commands
  • Data ExfiltrationExternal Transmission, Env Variable Harvesting, File System Enumeration
  • Supply ChainUnpinned Dependencies, External Script Fetching, Obfuscated Code
Findings (10)

Tool Parameter Abuse

High
Category
Tool Misuse
Content
- list: `GET /rest/api/3/issue/{key}/worklog`
- add: `POST /rest/api/3/issue/{key}/worklog`
- update: `PUT /rest/api/3/issue/{key}/worklog/{id}`
- delete: `DELETE /rest/api/3/issue/{key}/worklog/{id}`

Query params for update/delete:
- `adjustEstimate=auto|new|leave`
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Credential Access

High
Category
Privilege Escalation
Content
## Auth

- Prefer **Basic auth** with Atlassian API token.
- In this workspace, credentials are typically stored in `~/.netrc`:
  - machine: `<your-domain>.atlassian.net`
  - login: `<email>`
  - password: `<api_token>`
Confidence
80% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Auth

- Prefer **Basic auth** with Atlassian API token.
- In this workspace, credentials are typically stored in `~/.netrc`:
  - machine: `<your-domain>.atlassian.net`
  - login: `<email>`
  - password: `<api_token>`
Confidence
80% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Auth

- Prefer **Basic auth** with Atlassian API token.
- In this workspace, credentials are typically stored in `~/.netrc`:
  - machine: `<your-domain>.atlassian.net`
  - login: `<email>`
  - password: `<api_token>`
Confidence
80% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
## Auth

- Prefer **Basic auth** with Atlassian API token.
- In this workspace, credentials are typically stored in `~/.netrc`:
  - machine: `<your-domain>.atlassian.net`
  - login: `<email>`
  - password: `<api_token>`
Confidence
80% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Tool Parameter Abuse

High
Category
Tool Misuse
Content
### Worklogs

- List: `GET /rest/api/3/issue/{issueKey}/worklog`
- Delete: `DELETE /rest/api/3/issue/{issueKey}/worklog/{worklogId}?adjustEstimate=auto`
- Update: `PUT /rest/api/3/issue/{issueKey}/worklog/{worklogId}`
  - Body supports fields like `timeSpent`, `started`, and `comment` (ADF).
Confidence
80% confidence
Finding
Tool parameters are crafted to achieve unintended or unsafe behavior. Parameter abuse can bypass intended safety checks (e.g. shell=True, --force, dangerous glob patterns).

Credential Access

High
Category
Privilege Escalation
Content
def _netrc_auth_for_host(hostname: str, netrc_path: str = DEFAULT_NETRC) -> tuple[str, str]:
    if not os.path.exists(netrc_path):
        raise FileNotFoundError(f".netrc not found: {netrc_path}")
    n = netrc.netrc(netrc_path)
    auth = n.authenticators(hostname)
    if not auth:
Confidence
80% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def _netrc_auth_for_host(hostname: str, netrc_path: str = DEFAULT_NETRC) -> tuple[str, str]:
    if not os.path.exists(netrc_path):
        raise FileNotFoundError(f".netrc not found: {netrc_path}")
    n = netrc.netrc(netrc_path)
    auth = n.authenticators(hostname)
    if not auth:
Confidence
80% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Credential Access

High
Category
Privilege Escalation
Content
def _netrc_auth_for_host(hostname: str, netrc_path: str = DEFAULT_NETRC) -> tuple[str, str]:
    if not os.path.exists(netrc_path):
        raise FileNotFoundError(f".netrc not found: {netrc_path}")
    n = netrc.netrc(netrc_path)
    auth = n.authenticators(hostname)
    if not auth:
Confidence
80% confidence
Finding
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.

Natural-Language Policy Violations

Medium
Confidence
95% confidence
Finding
The skill contains user-facing instructions primarily in Spanish, including usage guidance, warnings, and examples, but does not indicate that the language is optional or provide an alternative. This can violate a language/locale policy when users are not explicitly opted into Spanish-only documentation.

Static analysis

No suspicious patterns detected.