Jira
MicrosoftOfficialPublished by Microsoft
- Skill
- Tool integration
- Automation and integrations
- MIT
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
The Jira skill provides a secure, Python-based command-line interface for interacting with Jira Cloud, Server, and Data Center instances. It enables users to perform common issue management tasks such as searching with JQL, retrieving issue details, creating or updating work items, transitioning issue statuses, and adding comments. The skill includes robust support for credential management, audit logging, and field discovery, ensuring that interactions with Jira are both traceable and well-structured.
What it does
- Search for issues using JQL with support for custom field extraction.
- Create and update Jira issues using JSON payloads.
- Transition issues between workflow statuses by name or ID.
- Add and list comments on specific issues.
- Discover project-specific issue types and required fields.
- Maintain an audit trail of all API requests for operational transparency.
When to use it
- When you need to automate issue tracking or status updates within a Jira project.
- When you need to perform bulk searches or extract specific issue data for reporting.
- When you are managing Jira workflows and need a programmatic way to transition tasks.
- When you need to integrate Jira issue management into an agent-driven development workflow.
Advantages
- Supports both Jira Cloud and Data Center/Server authentication methods.
- Provides a structured, audit-ready approach to API interactions.
- Allows for concise, field-specific output using dot-notation.
- Includes built-in safety mechanisms for credential handling and write confirmation.
Usage examples
- “Search for my active bugs in the PROJ project.”
- The agent executes a JQL query to return a compact table of issues assigned to the current user with a status of 'Bug' in the specified project.
- “Transition issue PROJ-123 to In Progress.”
- The agent triggers the transition command to move the specified issue to the 'In Progress' workflow state.
- “What are the required fields to create a new Task in PROJ?”
- The agent queries the Jira API to list the mandatory fields and issue types for the specified project, helping you construct a valid creation payload.
- “Add a comment to PROJ-456 saying the fix is deployed.”
- The agent posts the specified text as a comment to the issue PROJ-456.
Install
8 of its files are not mirrored here, so install it from its source:
https://github.com/microsoft/hve-core/tree/f425e943bf228052a23e6bc4810b5faef030314f/.github/skills/project-planning/jira
Documentation by Microsoft
Jira Skill
Overview
This skill provides a Python CLI for common Jira REST API workflows:
Search with JQL
Get issue details
Create and update issues with JSON payloads
Transition issues by name or ID
Add comments and list existing comments
Discover issue types and required fields for creation
The skill supports Jira Cloud with email plus API token authentication and Jira Server or Data Center with a personal access token.
Use --fields on read commands by default to keep output concise. The script supports dot-notation such as fields.status.name and prints tab-separated output for lists.
Prerequisites
Set the required environment variables before running the script.
| Platform | Runtime |
|---|---|
| Cross-platform | Python 3.11+ |
Authentication Variables
| Variable | When required | Purpose |
|---|---|---|
JIRA_BASE_URL | Always | Jira base URL, for example https://company.atlassian.net |
JIRA_USER_EMAIL | Jira Cloud | Account email used for basic authentication |
JIRA_API_TOKEN | Jira Cloud | API token paired with the Jira Cloud email |
JIRA_PAT | Jira Server or Data Center | Personal access token used for bearer authentication |
JIRA_CLOUD_TOKEN_MODE | Jira Cloud optional | unscoped (default) or scoped |
JIRA_CLOUD_ID | Scoped Jira Cloud token | Atlassian Cloud resource identifier |
Authentication is selected automatically:
If
JIRA_PATis set, the script uses bearer authentication for Jira Server or Data Center.Otherwise, the script expects
JIRA_USER_EMAILandJIRA_API_TOKENfor Jira Cloud.Scoped Cloud mode routes only to
https://api.atlassian.com/ex/jira/{cloudId}; mixed Cloud and Data Center credentials fail closed.
Scoped Cloud Token Setup
Scoped mode uses the same Basic authentication variables as unscoped mode and
adds a Cloud ID. It is not the OAuth 3LO flow, so the
oauth/token/accessible-resources endpoint is not a discovery or introspection
step for these credentials.
Create a scoped API token for your Atlassian account and select its scopes during creation. See Manage API tokens for your Atlassian account.
Choose scopes for the commands you intend to run.
read:jira-workcoverssearch,get,comments, andfields. Addwrite:jira-workforcreate,update,transition, andcomment. Granular scopes are listed in Jira scopes for OAuth 2.0 and Forge apps. Jira project permissions still apply independently of token scopes.Obtain the Cloud ID for your site from a trusted Atlassian administrator or your site's account record. The Cloud ID is not a secret, but it determines the request destination.
Confirm that the Cloud ID belongs to the same site as
JIRA_BASE_URL. The CLI cannot verify this association: in scoped mode it routes to the Atlassian resource API and does not useJIRA_BASE_URLas a destination. Treat the association as operator-maintained configuration.Set the variables, then confirm access with a read-only command:
export JIRA_BASE_URL="https://company.atlassian.net"
export JIRA_USER_EMAIL="[email protected]"
export JIRA_API_TOKEN="$(cat ~/.secrets/jira-token)"
export JIRA_CLOUD_TOKEN_MODE="scoped"
export JIRA_CLOUD_ID="your-cloud-id"
python scripts/jira.py --fields key search "ORDER BY created DESC" --max-results 1A successful read proves the token and route work together. It does not prove
that the Cloud ID corresponds to JIRA_BASE_URL, so verify that separately in
step 4.
Operational Variables
| Variable | When required | Purpose |
|---|---|---|
JIRA_AUDIT_LOG | Optional | Path to a JSON Lines audit log. When set, every request is audited (see Audit Logging). |
JIRA_AUDIT_ACTOR | Optional | Overrides the recorded actor identity (for example, a CI service principal). |
JIRA_DEBUG | Optional | Inherited environment only. Set to 1 to print a redacted traceback on failure. Never disables redaction. |
JIRA_ALLOW_INSECURE | Optional | Set to 1 to permit explicit loopback HTTP development endpoints only. |
JIRA_CONFIRM_WRITES | Optional | Inherited environment only. Set to 1 to satisfy the write confirmation gate without --confirm or --yes. |
Audit Logging
When JIRA_AUDIT_LOG is set, the script writes a structured JSON Lines audit trail for every API request. Auditing is fail-closed and write-ahead:
An
attemptrecord is written before the request is sent. If the audit log cannot be written, the operation is aborted and nothing is sent to Jira.An
outcomerecord (successorerror, with HTTP status on failure) is written after the request completes.
Each record includes a UTC timestamp, the actor (from JIRA_AUDIT_ACTOR, otherwise JIRA_USER_EMAIL or jira-pat), the operation, HTTP method, normalized origin, auth mode, and event. Resource paths, Cloud IDs, credentials, authorization headers, and query strings are excluded. Place this operationally sensitive file at an access-controlled path and retain it only as long as operations require. Audit failures after the request emit a warning without altering the result.
Credential Rotation
Atlassian Cloud API tokens have a selected lifetime of 1 to 365 days. Prefer a
scoped token and set JIRA_CLOUD_TOKEN_MODE=scoped with JIRA_CLOUD_ID.
Atlassian 3LO is not used because its token exchange requires a client secret.
For Jira Data Center, create a bounded PAT through the Jira UI and rotate it
out of band. The CLI does not issue PATs because the response would contain a
new secret without a transactional, non-model-visible sink.
Credential Setup
Run this workflow when credentials are missing, incomplete, or failing. It is verification-first and non-destructive: it audits the current state, guides acquisition, writes only non-secret values, and validates connectivity after the user supplies the credential themselves.
Safety boundary
These rules are not adjustable by autonomy mode or user request.
Never ask for, accept, or echo a token or PAT in chat. Display this warning whenever instructing the user to add one:
⚠️ NEVER paste your API token or PAT into this chat. Tokens entered here are sent through the AI model and are not secure. Edit the credentials file directly in the editor instead.Never write a credential value. Non-secret values (base URL, email) may be written; the token line is a placeholder the user replaces in their editor.
Never write credentials to a tracked file.
~/.jira.envlives in the user's home directory, outside any repository, so it cannot be accidentally committed. Do not write to.vscode/mcp.jsonor any repository file.Never modify a shell profile without explicit user confirmation.
Never display a token value or any part of one, including a prefix. A credential variable is reported only as set or missing.
Never run a command that prints an environment value.
printenv | grep -i JIRAand any other filtered dump are prohibited:grepselects which lines print, it does not mask them, so a live token reaches standard output and enters the transcript. Use the set-or-missing probe in the Protocol below instead. Never include raw credential values, full request headers, or verbose or trace HTTP output in a response, even while troubleshooting.Send nothing over the network until the user explicitly confirms connectivity testing.
Terminal session isolation
The agent's terminal and the user's terminal are separate sessions, so an export in one is invisible to the other. The CLI loads supported Jira variables from ~/.jira.env in-process before it creates a client. It treats every value as data, never evaluates the file as shell code, and does not overwrite a variable already inherited by the terminal.
JIRA_DEBUG and JIRA_CONFIRM_WRITES are inherited-environment-only control variables. An unconfirmed write is rejected before the credential file is read, so that file cannot grant mutation authority.
On POSIX platforms, the CLI requires the file to be owned by the current user and accessible only by that owner. It opens the path without following symlinks and in non-blocking mode, then rejects non-regular files, oversized files, malformed assignments, and files with group or other permissions before loading values. Windows does not expose equivalent owner and mode checks through this path; protect the file with user-only ACLs. Errors name the configuration problem without printing credential values.
The environment file bridges the sessions:
On POSIX, create the file with owner-only permissions by running
umask 077; touch ~/.jira.env; chmod 0600 ~/.jira.env. On Windows, create it under the user's profile and apply a user-only ACL. Fill in non-secret values and credential placeholders.Resolve and display the absolute path so the user knows exactly which file to edit.
Open it with
code ~/.jira.env.The user replaces the placeholders and saves.
Run the Jira CLI command. The CLI reads the file directly; do not source it.
Protocol
Audit. The CLI loads supported values from
~/.jira.env, so inherited-process probes alone are not a complete setup audit. Check whether the file exists without reading or printing it. For inherited variables only, classify known names as set or missing without printing any value:for v in JIRA_BASE_URL JIRA_USER_EMAIL JIRA_API_TOKEN JIRA_PAT JIRA_CLOUD_TOKEN_MODE JIRA_CLOUD_ID JIRA_AUDIT_LOG JIRA_AUDIT_ACTOR; do if printenv "$v" >/dev/null 2>&1; then echo "$v: set"; else echo "$v: missing"; fi done
printenv exits zero only when the inherited variable exists, and its output is discarded, so the classification never reveals a value. Use no modifying command during the audit. Let the Jira CLI's value-free validation identify variables still missing after in-process file loading.
2. Detect the platform. JIRA_PAT set indicates Server or Data Center. JIRA_USER_EMAIL with JIRA_API_TOKEN indicates Cloud. When mixed or ambiguous, ask which platform the user has rather than guessing, because the wrong choice produces an authentication failure that looks like a bad credential.
3. Validate what exists. Confirm JIRA_BASE_URL starts with https:// and flag a malformed value. Identify which required variables are missing for the detected platform.
4. Guide acquisition. Direct the user to their Atlassian account token page for Cloud, or their instance personal-access-token settings for Server or Data Center. Give the steps; never request the result.
5. Write the file. Create or update ~/.jira.env with non-secret values and credential placeholders, including a do-not-commit warning comment.
6. Validate connectivity. After the user confirms the credential is saved, run one read-only call. The CLI loads the file in-process. A 401 or 403 means the credential is wrong, expired, or revoked; a connection error means the base URL is wrong.
7. Summarize. Report what changed, what remains, and the set-or-missing state of each variable.
Completion
Setup is complete when every required variable for the detected platform is set, the base URL is well-formed, and one read-only call succeeds. Anything short of that is reported as incomplete with the specific remaining step, never as a qualified success.
Quick Start
Search for your current Jira issues and return a compact table:
python scripts/jira.py --fields key,fields.summary,fields.status.name search 'assignee = currentUser() ORDER BY updated DESC'Inspect one issue with a compact field list:
python scripts/jira.py --fields key,fields.summary,fields.status.name,fields.assignee.displayName get PROJ-123Create an issue from JSON piped through stdin:
cat <<'EOF' | python scripts/jira.py create
{
"fields": {
"project": { "key": "PROJ" },
"summary": "Fix login timeout on mobile",
"issuetype": { "name": "Bug" }
}
}
EOFParameters Reference
| Command or option | Syntax | Default | Description |
|---|---|---|---|
search | python scripts/jira.py search '<jql>' [max_results] | max_results = 50 | Search for issues with JQL |
get | python scripts/jira.py get <ISSUE-KEY> | None | Get one issue |
create | python scripts/jira.py create '<json>' | Reads stdin if omitted | Create an issue from JSON |
update | python scripts/jira.py update <ISSUE-KEY> '<json>' | Reads stdin if omitted | Update an issue from JSON |
transition | python scripts/jira.py transition <ISSUE-KEY> '<name-or-id>' | None | Move an issue to another workflow state |
comment | python scripts/jira.py comment <ISSUE-KEY> '<body>' | Reads stdin if omitted | Add a comment to an issue |
comments | python scripts/jira.py comments <ISSUE-KEY> [ISSUE-KEY ...] | None | List comments across one or more issues |
fields | python scripts/jira.py fields <PROJECT-KEY> [issue-type-id] | None | Discover issue types or required create fields |
--fields | --fields key,fields.summary,... | None | Extract selected fields from search, get, and comments output |
Script Reference
Search for Issues
Use bounded JQL for Jira Cloud queries. Include a project, assignee, sprint, or another filter instead of a bare ORDER BY query.
See JQL Reference for the query patterns this
skill expects.
python scripts/jira.py --fields key,fields.summary,fields.status.name search 'project = PROJ AND status = "In Progress"'
python scripts/jira.py --fields key,fields.summary search 'assignee = currentUser() ORDER BY updated DESC' 10Get One Issue
python scripts/jira.py --fields key,fields.summary,fields.priority.name,fields.status.name get PROJ-123Create an Issue
Discover valid issue types first:
python scripts/jira.py fields PROJInspect required fields for one issue type:
python scripts/jira.py fields PROJ 10045Create the issue:
python scripts/jira.py create '{
"fields": {
"project": { "key": "PROJ" },
"summary": "Document rollout checklist",
"issuetype": { "name": "Task" },
"labels": ["docs", "release"]
}
}'Update an Issue
python scripts/jira.py update PROJ-123 '{
"fields": {
"summary": "Updated summary",
"priority": { "name": "High" },
"labels": ["backend", "urgent"]
}
}'Transition an Issue
Use a transition display name or a numeric transition ID:
python scripts/jira.py transition PROJ-123 'In Progress'
python scripts/jira.py transition PROJ-123 31If a transition name is not found, the script returns the available transition names in the error output.
Comment on an Issue
python scripts/jira.py comment PROJ-123 'PR #42 addresses this issue.'
printf 'Deployed to staging.\n' | python scripts/jira.py comment PROJ-123List Comments
python scripts/jira.py --fields _issue,author.displayName,created,body comments PROJ-123 PROJ-456Troubleshooting
| Symptom | Likely cause | Resolution |
|---|---|---|
JIRA_BASE_URL is not set | Base URL is missing | Export JIRA_BASE_URL in the current shell |
| Authentication error | Wrong token or missing auth variables | Verify JIRA_PAT for Jira Server or Data Center, or verify JIRA_USER_EMAIL and JIRA_API_TOKEN for Jira Cloud |
Invalid issue key | Issue key format is malformed | Use keys in the form PROJ-123 |
| Transition not found | The requested workflow transition is unavailable | Re-run the command with the transition name returned in the error output |
| JSON payload error | Invalid JSON was passed to create or update | Validate the payload and retry with well-formed JSON |
| Network connection error | Jira instance URL is unreachable | Verify the base URL and local network access |
Files
- ATTRIBUTION.mdAdded by AutoGineer821 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SECURITY.md24.3 KiB
- SKILL.md18.8 KiB
- pyproject.toml680 B
- references/jql-reference.md3.4 KiB
- scripts/jira.pyScript44.4 KiB
- tests/conftest.pyScript4.4 KiB
- tests/corpus/0_error_payload(opens on GitHub)Not mirrored: opens on GitHub32 B
- tests/corpus/0_json_error(opens on GitHub)Not mirrored: opens on GitHub32 B
- tests/corpus/0_large(opens on GitHub)Not mirrored: opens on GitHub2.0 KiB
- tests/corpus/0_null_bytes(opens on GitHub)Not mirrored: opens on GitHub14 B
- tests/corpus/0_unicode_stress(opens on GitHub)Not mirrored: opens on GitHub13 B
- tests/corpus/1_empty1 B
- tests/corpus/1_issue_key9 B
- tests/corpus/1_long_key24 B
- tests/corpus/1_null_bytes(opens on GitHub)Not mirrored: opens on GitHub10 B
- tests/corpus/1_unicode_key11 B
- tests/corpus/1_valid_key9 B
- tests/corpus/2_deeply_nested604 B
- tests/corpus/2_empty1 B
- tests/corpus/2_field_path15 B
- tests/corpus/2_large_json2.0 KiB
- tests/corpus/2_nested_json30 B
- tests/corpus/2_unicode_fields26 B
- tests/corpus/3_comma_separated21 B
- tests/corpus/3_empty1 B
- tests/corpus/3_fields_csv19 B
- tests/corpus/3_large890 B
- tests/corpus/3_null_bytes(opens on GitHub)Not mirrored: opens on GitHub11 B
- tests/corpus/3_unicode_fields16 B
- tests/corpus/4_deeply_nested304 B
- tests/corpus/4_empty1 B
- tests/corpus/4_json_array10 B
- tests/corpus/4_json_object18 B
- tests/corpus/4_malformed_json9 B
- tests/corpus/4_trailing_garbage21 B
- tests/corpus/4_unicode_json17 B
- tests/corpus/README.md1.3 KiB
- tests/corpus/c45a54a39a931a07d4c7bccd0ca46a538e210cf3(opens on GitHub)Not mirrored: opens on GitHub20 B
- tests/fuzz_harness.pyScript5.5 KiB
- tests/test_constants.pyScript1.4 KiB
- tests/test_jira_audit.pyScript6.1 KiB
- tests/test_jira_commands.pyScript9.0 KiB
- tests/test_jira_coverage.pyScript7.1 KiB
- tests/test_jira_helpers.pyScript20.2 KiB
- tests/test_jira_main.pyScript7.7 KiB
- tests/test_jira_transport.pyScript16.7 KiB
- uv.lock57.5 KiB
SECURITY.md
title: Jira Skill Security Model description: STRIDE threat model for the Jira skill covering API egress, Cloud and Data Center credentials, structured output, audit logging, and caller-process boundaries author: microsoft/hve-core ms.date: 2026-09-04 ms.topic: reference estimated_reading_time: 10 keywords:
security
STRIDE
jira
rest cli
threat model
<!-- markdownlint-disable-file -->
Jira Skill Security Model
This document records the STRIDE threat model for the Jira skill (scripts/jira.py). The model is organized by trust bucket: CLI → Jira API (B1), environment and local-file credentials (B2), and CLI caller process (B3). Each bucket enumerates all six STRIDE categories with the in-code mitigations that address them. Assets and adversaries are enumerated first. Acknowledged enterprise readiness gaps are listed at the end.
The skill is a single-file, standard-library-only CLI. It performs no OAuth browser flow, runs no local listener, and spawns no subprocesses. Credentials are read from the process environment and may be loaded from an operator-managed local environment file without shell evaluation.
See also: repo-wide STRIDE model. This skill participates in the repository-wide threat model at
docs/security/security-model.mdand is registered in its Skill Security Models section.
Executive Summary
The Jira skill is a single-file, standard-library-only REST CLI. It reads an
expiring Cloud API token or Data Center PAT from the environment or an
operator-managed ~/.jira.env file. Scoped Cloud mode binds requests to the fixed Atlassian resource
origin; unscoped Cloud and Data Center retain validated configured origins.
Mixed modes fail closed. The optional audit sink records normalized origin and
auth mode and is operationally sensitive.
Security Posture Overview
| Dimension | Value |
|---|---|
| Runtime surface | REST CLI (stdlib only); env/file credentials; no listener, no subprocess |
| Trust buckets | B1 CLI→Jira API, B2 environment/local-file credentials, B3 caller process |
| Credentials | Expiring Cloud token or Data Center PAT from env or protected local file |
| Network egress | HTTPS to validated JIRA_BASE_URL or fixed https://api.atlassian.com |
| Open residual gaps | 7 (includes Windows credential-file ACL reliance) |
Contents
System Description
Trust Boundaries
Assets
Adversaries
Bucket B1: CLI → Jira API
Bucket B2: Environment and local-file credentials
Bucket B3: CLI caller process
Enterprise Readiness Gaps
References
System Description
Components
scripts/jira.py— a single-file CLI: parses arguments, resolves credentials from the environment, issues REST calls through a hardened opener, and prints JSON.Hardened opener (
_OPENER/_NoRedirect) — enforces TLS, refuses 30x redirects, and caps response bodies.
Data Flow
flowchart TD
subgraph HOST["Operator Workstation / Runner (trust zone)"]
CLI["jira.py CLI"]
ENVCRED["Cloud token / PAT / base URL<br/>Cloud mode + Cloud ID (env)"]
ENVFILE["Operator-managed ~/.jira.env<br/>literal assignments"]
OUT["JSON output / audit log"]
end
subgraph JIRA["Jira Instance (network boundary)"]
API["Jira REST API"]
end
subgraph ATLASSIAN["Atlassian Resource API (network boundary)"]
SCOPED["Scoped Jira Cloud REST API"]
end
ENVFILE -->|"bounded local file read"| CLI
CLI -->|"reads per invocation"| ENVCRED
CLI -->|"Bearer/Basic request (TLS, no-redirect)"| API
CLI -->|"Scoped Basic request (TLS, no-redirect)"| SCOPED
API -->|"issue payloads (untrusted)"| CLI
CLI -->|"writes"| OUTTrust Boundaries
Boundary Diagram
┌───────────────────────────────────────────────┐
│ TRUST BOUNDARY: Operator Workstation / Runner │
│ ┌─────────┐ ┌─────────────┐ ┌───────────┐ │
│ │ jira CLI │ │ Env/file │ │ JSON/audit │ │
│ │ │ │ credentials │ │ output │ │
│ └─────────┘ └─────────────┘ └───────────┘ │
└────────────────────────┬─────────────────────┘
│ HTTPS (TLS, no-redirect)
┌──────────────▼─────────────────────────┐
│ BOUNDARY: Jira Instance │
│ Jira REST API │
└────────────────────────────────────────┘
┌────────────────────────────────────────┐
│ BOUNDARY: Atlassian Resource API │
│ Scoped Jira Cloud REST API │
└────────────────────────────────────────┘Boundary Descriptions
| Boundary | Assets Protected | Controls Enforced |
|---|---|---|
| Operator Workstation / Runner | API token, environment file, output | In-process literal parsing; POSIX owner/mode checks; non-blocking no-follow open; redaction; write-confirm gate |
| Jira Instance | Request/response integrity, bearer token | TLS (system trust store); _NoRedirect; origin-only base URL; capped JSON parser |
| Atlassian Resource API | Scoped token, Cloud request integrity | Fixed HTTPS origin; single-segment Cloud ID; _NoRedirect; capped JSON parser |
Assets
| Id | Asset | Lifetime | Notes |
|---|---|---|---|
| A1 | Jira API token / PAT | Operator-managed | Read from JIRA_API_TOKEN / PAT env at invocation. Sent as Bearer (Server/DC) or HTTP Basic (email:token base64, Cloud) in Authorization. |
| A2 | Jira destination configuration | Operator-managed | Scoped Cloud uses the fixed Atlassian resource origin; other modes use validated JIRA_BASE_URL. |
| A3 | Request/response bodies, issue payloads | Command lifetime | Server responses may include issue text, comments, and field data authored by other users; downstream automation must treat as untrusted. |
| A4 | Diagnostic / audit output | Command lifetime | stderr diagnostics and the optional audit log; must never contain unredacted secrets. |
| A5 | Local Jira environment file | Operator-managed | Optional ~/.jira.env containing supported assignments; read as data without shell evaluation. On Windows, ACL enforcement remains operator-owned. |
Adversaries
| Id | Adversary | In-scope mitigations |
|---|---|---|
| ADV-a | Same-uid malware on the operator workstation | Not defended. A process running as the operator can read the environment directly. Workstation hygiene is the controlling defense. |
| ADV-b | Network attacker on the CLI ↔ Jira channel | TLS with stdlib certificate validation; HTTP redirects refused (_NoRedirect); HTTPS required for non-loopback hosts; capped, content-type-checked response parser. |
| ADV-c | Hostile or malformed Jira server / response | No-redirect opener; response size cap (MAX_BODY_BYTES); JSON content-type fail-closed; error bodies parsed-then-redacted before display. |
| ADV-d | Hostile caller process controlling argv / stdin / env | Inputs validated and URL-encoded; JIRA_BASE_URL canonicalized to origin-only; Basic-auth components ASCII-validated; stdin/JSON size-capped before parse. |
| ADV-e | Different local user or substituted file path | POSIX current-user ownership and mode checks; no-follow, non-blocking descriptor open; regular-file validation. Windows relies on user-only ACLs. |
Bucket B1: CLI → Jira API
REST calls use urllib.request through a hardened opener. Scoped Cloud calls
target the fixed Atlassian resource origin; unscoped Cloud and Data Center calls
target validated JIRA_BASE_URL.
Spoofing
TLS certificate validation is enforced by the stdlib default
SSLContext(system trust store).JIRA_BASE_URLis validated and normalized to an origin-only URL, rejecting embedded userinfo so a crafted value cannot impersonate a host with inline credentials (_validate_base_url/ base-URL normalization).Scoped Cloud mode treats
JIRA_CLOUD_IDas one bounded opaque path segment underhttps://api.atlassian.com; it cannot replace the request origin.
Tampering
TLS protects request and response bodies in transit.
The shared opener
_OPENERis built with_NoRedirect, which raises on any 30x so a redirect cannot silently retarget the request to another host.Interpolated path segments (project key, issue type id) are URL-encoded via
urllib.parse.quote(safe="");max_resultsis clamped; the issue key is matched againstISSUE_KEY_PATTERN.
Repudiation
Commands emit deterministic exit codes (
EXIT_SUCCESS/EXIT_FAILURE/EXIT_USAGE) so automation can attribute outcomes.An optional audit record contains the normalized destination origin and auth mode while excluding request paths and Cloud IDs. The sink is operationally sensitive because internal hosts can appear in it; see Enterprise Readiness Gaps for integrity limitations.
Information Disclosure
The Bearer/Basic token is sent only in the
Authorizationheader over TLS and is never logged.The token-bearing opener blocks 30x, preventing a hostile redirect from forwarding the
Authorizationheader to a non-Jira origin (_NoRedirect).The response parser requires a JSON content type when JSON is expected and reads through a capped reader (
_read_response_body,_get_response_content_type); a missing or non-JSON content type fails closed rather than being parsed as a token/payload.Remote error bodies are JSON-parsed first and only redacted for presentation (
_extract_error_message→_redact), so structured extraction is preserved and secrets in error text are masked._redactcoversBearer/Basic,Authorization/Proxy-Authorization/X-API-Key/PRIVATE-TOKEN/Cookie/Set-Cookie, and query-string secrets (token,api_key,access_token,private_token). It additionally applies_REDACT_PATTERNS, which mask every_REDACT_KEYSentry in JSON shape ("key": "value"), bare form shape (key=value, with no leading?or&required), and Azure Blob SAS query strings.code_challengeis deliberately excluded because a PKCE challenge is public by design and masking it would corrupt an authorization URL.Transport failures raise
JiraAPIError, whose__str__renders only the status, method, query-stripped resource, redacted summary, and a character-validated request ID. The raw response body, the query-bearing request URL, and the header dictionary are never part of the rendered string (_scrub_url,_response_request_id).JiraClient.auth_headercarriesrepr=False, sorepr(client), f-string interpolation, and traceback frame dumps cannot surface theBearer <pat>orBasic <base64(email:api_token)>credential.
Denial of Service
Response bodies are read through
_read_response_bodywith aMAX_BODY_BYTEScap so a runaway upstream cannot exhaust memory.The request uses
REQUEST_TIMEOUT_SECONDSso a stalled server cannot hang the CLI indefinitely.
Elevation of Privilege
The skill issues only the operations exposed by its explicit subcommands; there is no dynamic endpoint construction beyond validated, encoded path segments.
TLS posture
The skill performs every Jira call through the stdlib opener with no custom SSLContext, CA-bundle flag, or certificate pinning. Operators inherit Python's default HTTPS behavior: validation uses the system trust store; custom internal CAs require SSL_CERT_FILE/SSL_CERT_DIR; there is no pinning or mTLS (recorded as G-TLS-1). HTTPS is required for non-loopback hosts; plaintext http:// is refused even when JIRA_ALLOW_INSECURE=1 is set. The bypass is limited to loopback hosts only. Write operations such as create, update, transition, and comment require explicit confirmation via --confirm/--yes or inherited JIRA_CONFIRM_WRITES=1 before the credential file is read or a request is dispatched.
Risk Rating
| Threat | Likelihood | Impact | Residual Risk | Status |
|---|---|---|---|---|
| TLS MITM / hostile redirect retargeting | Low | High | Low | Mitigated (TLS + _NoRedirect) |
| Plaintext HTTP to a non-loopback host | Low | High | Low | Mitigated (refused) |
| Unconfirmed write operation | Low | Med | Low | Mitigated (confirm gate) |
| Oversized-response memory exhaustion | Low | Low | Low | Mitigated (body cap + timeout) |
Bucket B2: Environment and local-file credentials
Credentials and the instance origin are read from the process environment per invocation (JiraClient.from_environment). Before client construction, the CLI may load absent supported Jira variables from the operator-managed ~/.jira.env file. The parser reads assignments in-process without shell evaluation, preserves inherited variables, and never logs loaded values.
Spoofing
The base URL is parsed with
urllib.parse.urlsplitand must usehttp/https; HTTPS is enforced for non-loopback hosts.
Tampering
The optional environment file must be a regular non-symlink file. On platforms with POSIX permission bits, group or other access is rejected before reading.
On POSIX, the opened file must be owned by the current user. The descriptor is opened with no-follow and non-blocking flags where supported, then checked as a regular file before reading.
The environment parser accepts assignments as literal data, limits file size, rejects malformed or unterminated assignments, and ignores keys outside the supported Jira variable set.
JIRA_BASE_URLis rejected when it contains control characters, embedded userinfo, a query, a fragment, or a non-root path — it is reduced to an origin-only URL before any request is built.Cloud mode must be
unscopedorscoped; scoped mode requires a validated single-segment Cloud ID, and PAT/Cloud credential conflicts fail before authorization-header construction.The HTTP Basic credential is constructed from validated components and the resulting header is ASCII-only, preventing header injection via newline- or control-bearing email/token values.
Repudiation
Missing or malformed credentials fail fast with a
ScriptErrorand a usage exit code, so a misconfigured environment is attributable.
Information Disclosure
The CLI never writes tokens to disk or logs values loaded from the operator-managed environment file. The Basic-auth base64 value is used only to build the
Authorizationheader.Audit records exclude the Cloud ID, request path, query, and credential material.
Denial of Service
The environment file is size-capped. Non-blocking open prevents a substituted FIFO from waiting for a writer on platforms that support the flag.
Elevation of Privilege
The token's effective permissions are governed entirely by Jira; the skill adds no privilege and cannot broaden scope.
Risk Rating
| Threat | Likelihood | Impact | Residual Risk | Status |
|---|---|---|---|---|
| Header injection via credential components | Low | Med | Low | Mitigated (ASCII validation) |
| Base-URL host impersonation (embedded userinfo) | Low | Med | Low | Mitigated (origin-only) |
| Different-owner or non-regular credential file | Low | High | Low on POSIX; Med on Windows | Mitigated on POSIX; operator-controlled ACL on Windows |
Bucket B3: CLI caller process
The caller controls argv, environment, stdin, stdout, and stderr; the CLI treats that process as operator-controlled.
Spoofing
The CLI has no network listener or attach surface; it runs as the invoking OS user.
Tampering
Arguments are parsed with
argparse; handlers validate identifiers and encode interpolated segments before issuing requests.JSON arguments are parsed through the JSON-argument helper, which enforces a size cap before
json.loadsso an oversized payload cannot be materialized in memory.
Repudiation
Validation, authentication, and runtime failures map to distinct exit codes so a calling step can attribute the failure class.
Information Disclosure
Command output is JSON-encoded Jira payloads; tokens never appear in normal output.
Every process-stream write is funneled through
_emit(stderr plus thejiralogger),_emit_stdout(stdout), or_emit_debug_traceback, and each applies_redactbefore writing. A source-contract test asserts noprintcall exists outside those three helpers, so stdout cannot bypass the barrier that stderr passes.JIRA_DEBUG=1enables a traceback on the failure path; the formatted traceback is redacted before it is written.LOGGER.exceptionis banned by a source-contract test because it would emit an unredacted traceback.Jira-authored text returned in output must be treated as untrusted by downstream automation; the CLI never interpolates it into model instructions.
Denial of Service
stdin and JSON payloads are size-capped before parsing;
max_resultspagination is clamped to a bounded value.
Elevation of Privilege
There is no command path that bypasses input validation or constructs an unencoded request URL from caller input.
Risk Rating
| Threat | Likelihood | Impact | Residual Risk | Status |
|---|---|---|---|---|
| Oversized stdin / JSON payload | Low | Low | Low | Mitigated (size caps) |
| Untrusted Jira text consumed downstream | Med | Med | Med | By design (consumer responsibility) |
| Leaked token not revocable by the skill | Low | High | Med | Accepted upstream (G-EOP-1) |
Enterprise Readiness Gaps
The following are known limitations recorded so operators can make informed deployment decisions. Severity ratings are the project's own assessment and are not equivalent to a CVSS score.
| Id | Gap | Severity | Status |
|---|---|---|---|
| G-REP-1 | The write-ahead attempt is fail-closed, but the outcome is best-effort and the operator-supplied file is not signed or append-only. | Repudiation-Med | By design; integrate with host telemetry for tamper-evident logging. |
| G-INF-1 | Redaction is regex-based and intentionally broad; it may over-redact benign diagnostic text. It is a defense-in-depth backstop, not a license to log secrets. | InfoDisc-Low | Accepted; operators should still avoid logging credential-bearing values. |
| G-INF-2 | _REDACT_KEYS enumerates known credential field names; a novel or vendor-specific field name is masked only if it also matches the header, Bearer/Basic, or query-string rules. | InfoDisc-Low | Accepted; extend _REDACT_KEYS as new credential shapes appear. tests/test_jira_helpers.py pins the set. |
| G-EOP-1 | The skill cannot revoke a leaked expiring Cloud API token or Data Center PAT; revocation is performed at Atlassian or the Jira instance. | EoP-Med | Upstream control; rotate or revoke at the provider on suspicion of compromise. |
| G-SUP-1 | The skill's Python dependencies are declared in pyproject.toml but transitive hashes are not pinned and no SBOM is published for the skill; transport/URL/auth fuzz coverage is partial. | SupplyChain-Med | Tracked at the repository level. |
| G-TLS-1 | No certificate pinning for the Jira origin; TLS validation depends entirely on the system trust store. | InfoDisc-Low | Operator-acceptable for a managed Jira endpoint; documented for customers whose policy mandates pinning. |
| G-TAM-1 | Windows does not expose POSIX ownership and mode checks through this loader, so credential-file access control depends on operator-managed user-only ACLs. | Tampering-Med | Platform residual; keep the file under the user profile and restrict its ACL to that user. |
For an active issue tracker entry covering these gaps, see microsoft/hve-core#2225.
References
Repository security model
🤖 Crafted with precision by ✨Copilot following brilliant human instruction, then carefully refined by our team of discerning human reviewers.
Requirements and compatibility
Written for: GitHub Copilot, Claude Code
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Claude Code frontmatter, Needs Python
The author says: Requires Python 3.11+ and Jira credentials in environment variables
Security
What our scanner noticed· Low risk
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Microsoft
- Publisher
- Microsoft (Official)
- Source
- microsoft/hve-core · .github/skills/project-planning/jira · commit f425e94
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Microsoft or Microsoft.
Get it from the source
8 of this skill’s files are not mirrored here, so there is no ZIP. Get the complete skill from its source: microsoft/hve-core