JIRA: Redact Sensitive Information from Issue Views
Redacts sensitive content from the responses of JIRA issue-view tools before they reach the caller.
- Direction
- egress
- Rego package
jira.egress.redact_sensitive_info- App
- jira
- Bundles
- atlassiansoc2gdpr-ccpa
- Published
- Minimum gateway
- 1.0.0b24
- Schema version
- 1.0.0
- Checksum
sha256:8d1f6a48db9d9572ec3f8ef34c4afd991dd1d9bb1cd9834febdec88f3716e600
jiraatlassianpiisecretsdlpredactionegresssoc2gdpr-ccpaiso27001-nist
What this policy does
Direction: egress (tool_post_invoke)
Default: allow (transform-only — never denies)
Package: jira.egress.redact_sensitive_info
What it does
Redacts sensitive content from the responses of JIRA issue-view tools before they reach the caller. The goal is durable: prevent PII, credentials, and secrets from leaking out through JIRA issue bodies, comments, worklogs, and linked-issue content.
It is a transform-only egress policy — it never denies a call, it only rewrites
matching content in the response to [REDACTED]. Any tool that is not a JIRA
issue-view tool passes through untouched.
Compliance alignment
This policy instantiates egress PII/secrets redaction (family PF-02, with a card-number masking slice of family PF-01) and supports alignment with:
- SOC 2 CC6.7, P6.1 — restricts the transmission/movement of confidential and personal information out of Jira issue content, and reduces personal-information disclosure in agent-visible responses.
- HIPAA §164.514(d), §164.514(a)–(b) — minimum-necessary support: masks identifiers (SSN, email, phone) on the egress path; partial support for de-identification of identifier classes in issue text.
- PCI DSS 3.4.1 — masks card-number-shaped values in displayed responses (pattern-based, not Luhn-validated — see Known limitations).
- GDPR Art. 5(1)(c), Art. 9; CPRA §1798.121, §1798.150 — data minimisation, sensitive-personal-information limitation, and reduced nonredacted-PI breach exposure on the agent channel.
- ISO 27001 A.8.11, A.8.12 — data masking and data-leakage prevention on tool responses.
Why egress and not ingress
The risk here is reading secrets that already live inside JIRA issues (pasted into a description, a comment, or a worklog). Those values exist regardless of this gateway, so there is nothing to block at ingress — the leak happens when the content is returned to an MCP client. Masking on the egress (response) path is the only place to catch it.
Scope / tool matching
The policy applies only to JIRA tools that surface issue-body content, matched
by tool-name suffix so it stays portable regardless of the MCP server name
prefix the gateway adds (atlassian-, atlassian-jira-mcp-, etc.):
*-getjiraissue*-searchjiraissuesusingjql*-getcommentsforjiraissue*-getjiraissuecomments*-getworklogsforjiraissue*-getjiraissueworklog*-getjiraissueremoteissuelinks
The tool name is read from both input.resource.name and
input.tool_metadata.name, so the policy matches regardless of which surface
the gateway populates first. Confirm the exact tool names your gateway sends
using the dump-input debug technique before relying on this in production; if
your JIRA MCP server exposes other issue-view tools, add their suffixes to
view_tool_suffixes.
What gets redacted
Redaction works two ways. By pattern in any string value:
- PII — US SSN, credit card numbers, email addresses, US phone numbers
- Cloud / SaaS keys (vendor-prefixed shapes) — AWS access keys (
AKIA…,ASIA…), Google API keys (AIza…) and OAuth tokens (ya29.…), GitHub tokens (ghp_,gho_,ghu_,ghs_,ghr_), GitLab PATs (glpat-…), Slack tokens (xox[abprs]-…), Stripe keys (sk_live_,sk_test_,pk_live_,pk_test_) - OAuth / bearer —
Authorization: Bearer …headers and JWTs - Generic
key: value/key=valuesecret assignments (api_key, password, secret, token, client_secret, credentials, …) - Database connection strings with embedded credentials (postgres, mysql,
mongodb, redis, amqp, mssql/sqlserver URIs; JDBC strings; ADO.NET-style
Server=…;User Id=…;Password=…) - PEM private-key blocks (
-----BEGIN … PRIVATE KEY----- … -----END …-----)
And by field name — any response field whose key is one of password,
passwd, pwd, secret, api_key, apikey, token, access_token,
refresh_token, id_token, client_secret, private_key,
connection_string, or credentials.
Matches are replaced with [REDACTED].
Examples
Redacted (JIRA issue view)
{
"input": {
"action": "tool_post_invoke",
"resource": { "name": "atlassian-jira-mcp-getjiraissue", "type": "tool" },
"tool_metadata": { "name": "atlassian-jira-mcp-getjiraissue" }
}
}
allow = true, with a transform that supplies the redaction patterns,
field names, and replacement = "[REDACTED]" for the gateway to apply to the
response body, plus reason = "Sensitive content redacted from Jira response"
so the redaction is explained in the dashboard.
Passed through (any non-issue-view tool)
{
"input": {
"action": "tool_post_invoke",
"resource": { "name": "atlassian-jira-mcp-getjiraproject", "type": "tool" },
"tool_metadata": { "name": "atlassian-jira-mcp-getjiraproject" }
}
}
allow = true, no transform — the response is returned unchanged.
Composition
This policy is single-purpose and transform-only, so it composes cleanly with access-control policies on the same egress pipeline. Useful companions:
- An ingress policy that blocks writing secrets into JIRA in the first place (so new issues don't accumulate credentials).
- Equivalent egress redaction policies for other Atlassian apps (Confluence,
Bitbucket). See the
bundles/atlassianbundle for the curated Atlassian set.
Known limitations
- Regex over plain text. Detection is pattern-based, so novel or non-standard token formats, short-lived rotating tokens, and custom-shaped secrets may not be caught. Treat this as a high-signal layer, not a complete DLP solution.
- Scope is issue-view tools only. Other JIRA tools that might surface
issue content (e.g. bulk export or attachment-download endpoints) are not
inspected unless you add their suffixes to
view_tool_suffixes. - No identity-based exemptions. All callers get the same redaction. Add an
input.subject.claims-gated branch if a break-glass role needs raw values.
Compliance note. This policy supports alignment with the cited framework controls on the MCP path only. No policy or bundle makes an organization compliant with any framework; web-UI, native-API, and in-app access are outside the gateway's reach by design. Validate against your own compliance program before relying on it.
Policy source (Rego)
package jira.egress.redact_sensitive_info
# Transform-only policy — never denies, only redacts sensitive content.
# Scoped to JIRA tools that return issue-body content. Other tools (including
# non-JIRA egress) pass through this policy untouched.
default allow := true
# -----------------------------------------------------------------------------
# Scope: JIRA tools that surface issue content (summary, description, comments,
# worklog, custom fields, linked issues). Suffix matching makes this work
# regardless of the MCP server name (atlassian-, atlassian-jira-mcp-, etc.).
# -----------------------------------------------------------------------------
view_tool_suffixes := {
"-getjiraissue",
"-searchjiraissuesusingjql",
"-getcommentsforjiraissue",
"-getjiraissuecomments",
"-getworklogsforjiraissue",
"-getjiraissueworklog",
"-getjiraissueremoteissuelinks",
}
is_jira_issue_view if {
some suffix in view_tool_suffixes
name := lower(input.resource.name)
endswith(name, suffix)
}
is_jira_issue_view if {
# Egress hooks also expose the tool name under tool_metadata.name — check
# both so we match regardless of which surface the gateway populates first.
some suffix in view_tool_suffixes
name := lower(object.get(input.tool_metadata, "name", ""))
endswith(name, suffix)
}
# -----------------------------------------------------------------------------
# Redaction transform
# -----------------------------------------------------------------------------
transform := {
"redact_patterns": [
# ---- PII ----
`\d{3}-\d{2}-\d{4}`, # US SSN
`\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}`, # Credit card
`[\w.-]+@[\w.-]+\.[\w.-]+`, # Email
`\+?1?[- .]?\(?\d{3}\)?[- .]?\d{3}[- .]?\d{4}`, # US phone
# ---- Cloud / SaaS API keys (vendor-prefixed shapes) ----
`AKIA[0-9A-Z]{16}`, # AWS access key ID
`ASIA[0-9A-Z]{16}`, # AWS temporary (STS) access key
`AIza[0-9A-Za-z_-]{35}`, # Google API key
`ya29\.[0-9A-Za-z_-]+`, # Google OAuth access token
`ghp_[A-Za-z0-9]{36}`, # GitHub personal access token
`gho_[A-Za-z0-9]{36}`, # GitHub OAuth token
`ghu_[A-Za-z0-9]{36}`, # GitHub user-to-server token
`ghs_[A-Za-z0-9]{36}`, # GitHub server-to-server token
`ghr_[A-Za-z0-9]{36}`, # GitHub refresh token
`glpat-[A-Za-z0-9_-]{20}`, # GitLab personal access token
`xox[abprs]-[A-Za-z0-9-]+`, # Slack tokens (bot/app/user/refresh/etc.)
`sk_live_[A-Za-z0-9]{24,}`, # Stripe live secret key
`sk_test_[A-Za-z0-9]{24,}`, # Stripe test secret key
`pk_live_[A-Za-z0-9]{24,}`, # Stripe live publishable key
`pk_test_[A-Za-z0-9]{24,}`, # Stripe test publishable key
# ---- OAuth / bearer ----
`(?i)bearer\s+[A-Za-z0-9._~+/-]+=*`, # Authorization: Bearer <token>
`eyJ[A-Za-z0-9_-]+\.eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+`, # JWT (header.payload.signature, base64url)
# ---- Generic key/value secret patterns ----
`(?i)(?:api[_-]?key|apikey|secret[_-]?key)\s*[:=]\s*\S+`, # api_key=..., api-key: ...
`(?i)(?:password|passwd|pwd|secret|token|credentials|client[_-]?secret)\s*[:=]\s*\S+`,
# ---- Database connection strings ----
# URI form with embedded user:password (postgres, mysql, mongo, redis, amqp, mssql)
`(?i)(?:postgres(?:ql)?|mysql|mongodb(?:\+srv)?|redis(?:s)?|amqps?|mssql|sqlserver)://[^:\s]+:[^@\s]+@[^/\s]+(?:/\S*)?`,
`(?i)jdbc:[a-z0-9]+:[^\s]+`, # JDBC strings
`(?i)(?:Server|Data Source)\s*=\s*[^;]+;\s*(?:User Id|UID)\s*=\s*[^;]+;\s*(?:Password|PWD)\s*=\s*[^;]+`,
# ---- PEM private keys (multi-line, lazy match between BEGIN/END markers) ----
`-----BEGIN [A-Z ]+PRIVATE KEY-----.+?-----END [A-Z ]+PRIVATE KEY-----`,
],
"redact_fields": [
"password",
"passwd",
"pwd",
"secret",
"api_key",
"apikey",
"token",
"access_token",
"refresh_token",
"id_token",
"client_secret",
"private_key",
"connection_string",
"credentials",
],
"replacement": "[REDACTED]",
} if {
is_jira_issue_view
}
# Surfaced on the decision event whenever the redaction is in scope, so the
# dashboard can explain the rewrite.
reason := "Sensitive content redacted from Jira response" if {
is_jira_issue_view
} Canonical source: policy.md on GitHub · raw · raw on this site (.md)
Used in these guides
Related policies
Airtable: Redact PII in Record Reads
Scans the responses of the Airtable record-read tools — the calls that return row fields values — and rewrites high-confidence PII shapes to a fixed…
Asana: Redact PII in Task & Comment Reads
On the Asana MCP read path, this transform scans the free-text business fields that ride back in task, comment/story, and status-update responses — notes,…
BigQuery: Redact PII in Query Results
Scans the content returned by BigQuery's result-returning tools and rewrites high-confidence PII shapes to fixed, non-recoverable redaction tokens before the…
Block Agent Email to External Recipients
Blocks agent-initiated Microsoft 365 email sends when any recipient address falls outside a corporate-domain allowlist.
Block BigQuery Exfiltration and Cross-Project Writes
Inspects the raw GoogleSQL string carried by BigQuery SQL tools and denies any statement that moves data out of the tenant's own project — even when the call…
bigqueryguard-warehouse-exportingresssqlexfiltrationsoc2pci-dssgdpr-ccpa
Block Bulk Export & External Staging (Snowflake)
Blocks Snowflake SQL-execution tool calls whose query text moves whole tables off the Snowflake perimeter — bulk export to cloud storage or a stage, and…
snowflakeguard-warehouse-sqlexportexfiltrationingresssoc2pci-dssgdpr-ccpa