PowerShell automation scripts for linting and validation
GitHub Actions CI/CD workflows
VS Code extension packaging utilities
The Mural skill runtime: a Python CLI and embedded stdio MCP server with an OAuth client, local token store, and outbound HTTP egress to the Mural REST API
Most of the repository contains no runtime services, databases, or user data storage and is targeted primarily by supply chain and developer workflow threats.
The Mural skill is the exception: it executes locally, holds OAuth tokens in the OS keyring (or an encrypted file fallback), and makes authenticated requests to a third-party SaaS.
Threats specific to that runtime are analyzed in the OAuth Authentication Threats and MCP Server Trust Analysis sections.
Security relies on defense-in-depth with 20+ automated controls validated through CI/CD pipelines.
This section documents threats using STRIDE methodology (Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege), supplemented with AI-specific and Responsible AI threat categories.
OpenSSF Scorecard Token-Permissions flags security-events: write as overly broad across workflow files. This permission is required for github/codeql-action/upload-sarif and github/codeql-action/analyze to upload SARIF results to the repository Security tab. The security-events scope grants access only to code scanning alert data and cannot modify repository content, settings, or secrets.
Scorecard's own scorecard.yml requires the same permission to publish results, creating a circular dependency in the token-permissions check.
Affected workflow jobs:
Workflow
Job
release-stable.yml
dependency-pinning-scan
release-stable.yml
gitleaks-scan
pr-validation.yml
dependency-pinning-check
pr-validation.yml
workflow-permissions-check
pr-validation.yml
gitleaks-scan
pr-validation.yml
codeql
security-scan.yml
codeql
weekly-security-maintenance.yml
validate-pinning
weekly-security-maintenance.yml
codeql-analysis
Defense-in-depth controls:
All workflows declare job-level permissions, not workflow-level
persist-credentials: false set on all checkout steps
Inline YAML comments document each security-events: write declaration
SARIF upload is the only write operation performed under this permission
The meeting-analyst agent retrieves M365 transcripts containing sensitive data and writes them to local files in .copilot-tracking/. Data may be exposed through accidental commits (git add -f), gitignore misconfiguration, shared Codespaces, CI/CD logs, or unencrypted disk access.
Likelihood
Medium (users may not recognize transcript sensitivity; gitignore is the only barrier)
Impact
High (customer confidential data, PII, trade secrets)
Mitigations
Gitignore for .copilot-tracking/, agent-level data sensitivity notice and pre-flight classification prompt, anonymization guidance in agent instructions, data retention cleanup at handoff, documentation in threat model and agent catalog
Residual Risk
Medium (gitignore is not a security control; user awareness is behavioral)
Status
Partially Mitigated with Documentation
RAI-4: Inclusiveness - Exclusionary Language in Artifacts
Field
Value
Category
Inclusiveness (Responsible AI)
Asset
Prompt artifacts, documentation
Threat
Language in prompts excludes or marginalizes user groups
These threats address risks specific to the OAuth 2.0 Authorization Code + PKCE flow used by the Mural skill and apply to any future skill that authenticates against a third-party authorization server using a loopback redirect URI on the developer workstation.
The catalog uses an extended 11-row format that adds Source (verbatim citation), Trust Boundary Crossed, and Detection to the standard STRIDE row template.
Mural-specific facts are sourced from https://developers.mural.co/public/docs/oauth (fetched 2026-05-10).
Mural documentation contradiction: Mural's OAuth doc narrative claims refresh tokens are rotated, but the documented JSON response schema and reference paragraph confirm they are NOT ({ "access_token": ..., "expires_in": ... } only; "You can reuse your refresh_token as many times as you need"). The schema and reference paragraph are authoritative. OA-11 below is built on the verified non-rotation behavior; do not be misled by Mural's narrative.
OA-1: Authorization Server Phishing / Spoofed Consent Page
Field
Value
Category
Spoofing
Asset
User credentials, OAuth grant decision
Threat
Attacker directs the user to a look-alike Mural consent page (typosquatted domain or DNS hijack) and harvests credentials or coerces an OAuth grant for an attacker-controlled client
Likelihood
Low (requires user-side browser deception or DNS attack)
Impact
High (account takeover; attacker-issued tokens with full delegated scope)
Mitigations
Skill constructs the authorization URL from a hardcoded constant (https://app.mural.co/api/public/v1/authorization/oauth2/); HTTPS enforced; user instructed to verify URL bar before consenting; client_id is non-secret
Residual Risk
Low (deception happens outside the skill's trust boundary; relies on user vigilance and OS DNS integrity)
OA-2: Authorization Server Mix-Up via Missing iss Parameter
Field
Value
Category
Spoofing
Asset
Authorization-code-to-token exchange integrity
Threat
If the skill ever supports more than one authorization server, an attacker AS that the user has previously authorized could redirect a code from itself to Mural's token endpoint (or vice versa) and the client cannot distinguish the issuer because Mural does not return RFC 9207 iss
Likelihood
Very Low for current single-AS skill design; Medium if multi-AS support is added
Impact
High (cross-AS token confusion; attacker-controlled token usable against legitimate AS)
Mitigations
Skill is single-AS by design; per-request state enforcement (skill _run_login L2200, L2237) binds callback to issuing request; PKCE code_verifier (RFC 7636) cryptographically binds the code to this client and authorization request; do not add a second AS without first implementing RFC 9207 issuer validation or equivalent per-AS state-namespace
Residual Risk
Low for current design; would become Medium if multi-AS is added before mitigation
Authorization code in transit from browser to skill loopback handler
Threat
A co-resident process on the developer workstation binds the loopback port before the skill or races the bind, intercepting the authorization code delivered to http://127.0.0.1:<port>/callback
Likelihood
Low on single-user workstations; Medium on shared dev hosts and Codespaces with port forwarding
Impact
High (intercepted code can be exchanged for tokens until single-use enforcement triggers; PKCE prevents exchange but only if the attacker lacks the verifier)
Mitigations
Loopback handler binds before authorization request is opened (_start_loopback_server L2087); ephemeral port; PKCE binds the code to this client's code_verifier so an interceptor without the verifier cannot exchange the code; redirect URI validated against an allow-list (_validate_redirect_uri L2110, _resolve_redirect_uri L2148)
Residual Risk
Low (PKCE is the load-bearing control; the verifier is held only in-process and never logged via _REDACT_KEYS)
EADDRINUSE on bind; loopback handler logs unexpected callbacks; second invalid_grant ("already used") on token exchange attempt
OA-4: Client Impersonation via Leaked client_secret
Field
Value
Category
Spoofing
Asset
Mural-issued client_secret for the registered OAuth application
Threat
Mural documents only the confidential-client OAuth flow (no public-client / PKCE-only path), so the skill must hold a client_secret. If that secret leaks (env-var dump, log capture, file-permission downgrade, accidental commit, screen share), an attacker can impersonate the registered client and complete token exchanges for any user-issued authorization code
Likelihood
Low (skill enforces 0600 file permissions and redacts secrets from logs)
Impact
Critical (full client impersonation; attacker can mint tokens for any user who completes the OAuth dance against the legitimate AS)
Mitigations
_check_credential_file_perms L530 enforces 0600 mode on the credential file; _REDACT_KEYS L140 includes client_secret and is exercised by _redact() L1332 across all log-emission paths; secret never written to stdout; documented rotation runbook in skill SECURITY.md G-EOP-1; lint rule prohibits hardcoded credentials
Residual Risk
Low (depends on _REDACT_KEYS test coverage; Q3=a parallel work item adds the missing test_redaction.py to lock the contract)
Status
Mitigated
Source
RFC 6749 §2.3.1 (Client Password); RFC 6819 §4.1.1 (Threat: Obtaining Client Secrets); Mural verbatim: "client_secret: The secret key you copied when you created your app in Mural."
Trust Boundary Crossed
Skill Process ↔ Token Cache File; Skill Process ↔ Log Sinks
Authorization-request integrity; binding of callback to legitimate user session
Threat
Attacker tricks the user's browser into issuing a forged callback containing an attacker-issued authorization code, causing the skill to bind the user's local session to an attacker's Mural account (cross-account login CSRF) or to honor an attacker-tampered redirect_uri / scope
Likelihood
Low when skill enforces state; Medium if state enforcement is dropped because Mural marks state optional
Impact
High (cross-account binding; data exfiltration to attacker's Mural workspace; or scope upgrade)
Mitigations
Skill MUST enforce state regardless of Mural's "optional" classification; _run_login generates and verifies state at L2200 and L2237; redirect_uri is allow-listed via _validate_redirect_uri L2110; scope is constructed from a hardcoded constant; PKCE binds the code to the client
Residual Risk
Low (assuming state enforcement remains; regression test recommended; see Phase 5 follow-on work)
Status
Mitigated
Source
RFC 6749 §10.12 (Cross-Site Request Forgery); OAuth 2.1 §4.1.1 (state REQUIRED); RFC 9700 §4.7 (CSRF on Redirect URI); Mural verbatim (note marks state as optional, contradicting OAuth 2.1): "state: A value that you randomly generate and store. (This is optional, but recommended.)"
Trust Boundary Crossed
Browser ↔ Skill Process (loopback)
Detection
state mismatch in _LoopbackHandler callback; logged as security event (state value itself is not logged; only the mismatch fact)
Attacker who observes an authorization code (in browser history, referer header, log scrape, or screen capture) attempts to exchange it a second time at the token endpoint
Likelihood
Low (Mural enforces single-use server-side; PKCE additionally requires the verifier)
Impact
High if replay succeeds (attacker tokens issued to attacker client)
Mitigations
Mural enforces single-use codes; PKCE code_verifier binds the exchange to this client; skill exchanges the code immediately on receipt and never retains it; code is in _REDACT_KEYS so it is never logged; authorization-code TTL (V8) is undocumented but bounded by single-use and the prompt-revoke runbook
Residual Risk
Very Low
Status
Mitigated
Source
RFC 6819 §4.4.1.1 (Threat: Eavesdropping or Leaking Authorization Codes); RFC 7636 §1 (PKCE); Mural verbatim: "If the provided authorization grant (code) or refresh token is invalid, already used, expired, revoked, does not match the redirect_uri used in the authorization request, or was issued to another client, you will receive ... invalid_grant"
Trust Boundary Crossed
Skill Process ↔ Mural Token Endpoint
Detection
invalid_grant with "already used" semantics on second exchange; monitor token-endpoint error rate
A user repudiates an OAuth grant or token-issued action because the skill emits no client-side audit record, and the Mural-side audit trail is the only source of truth
Likelihood
Medium (the skill writes operational logs but does not emit a structured audit event for OAuth lifecycle transitions)
Impact
Medium (forensic investigation must rely entirely on Mural-side logs; correlation with local client activity is impossible)
Mitigations
Skill emits structured logger events for login_completed, token_refreshed, token_revoked; Mural-side audit log retrieved via account-side review at https://app.mural.co/account/api; correlation via per-request state value (logged as opaque ID, not value)
Residual Risk
Medium (client-side audit log is operator-managed and not centralized; recommend SIEM forwarding for high-assurance deployments; see Phase 5 follow-on)
Status
Partially Mitigated
Source
RFC 6819 §5.1.4 (Audit and Trail Threats); NIST SP 800-92 (Guide to Computer Security Log Management); OWASP ASVS V8.3 (Logging and Monitoring)
Trust Boundary Crossed
Skill Process ↔ Log Sinks; Skill Process ↔ Mural API
Detection
Out-of-band review of Mural API audit log; gap analysis between client-side log timestamps and Mural-side events
OA-8: Token / Secret Leakage via Application Logs
A high-severity log line emits a request body, response body, header dictionary, exception traceback, or URL containing one of the sensitive fields above; the value lands in operator log files, CI logs, or remote log aggregators
Likelihood
Medium (Python developers commonly LOGGER.error("Request failed: %s", response.text) without thinking about token contents)
Impact
Critical (token reuse against Mural API for the lifetime of the token; refresh tokens are non-rotated per OA-11 and remain valid until manual revocation)
Mitigations
Centralized _redact() L1332 pipes all loggable structures through _REDACT_KEYS L140; skill convention forbids direct LOGGER.* calls on response bodies / request bodies / URLs; _REDACT_KEYS test (test_redaction.py) locks the key list; instructions file mural-log-hygiene.instructions.md is mandatory reading for any skill change
Residual Risk
Medium pending _REDACT_KEYS expansion (Q3=a) and audit of remaining direct LOGGER call sites (mural.py L1509, L1746, L4128, L4143, L5064, L5071, L9271; print(authorize_url) L2228; lowercase loggers L95, L103, L110)
Status
Partially Mitigated (active remediation tracked under Phase 5 follow-on work)
Source
RFC 6819 §5.1.6 (Threat: Information Leakage); RFC 9700 §2.6 (Token Storage and Handling); OWASP ASVS V7.1 (Log Content Requirements); MITRE ATT&CK T1552.001 (Credentials in Files)
Trust Boundary Crossed
Skill Process ↔ Log Sinks
Detection
Pre-merge gitleaks scan; static-analysis rule for LOGGER\.(debug|info|warning|error|exception)\(.*\\b(response|request|url|body|headers|token|secret|code)\\b patterns; SIEM alert on Mural-token regex in log streams
OA-9: Token Leakage via Browser Referer / History
Field
Value
Category
Information Disclosure
Asset
Authorization code; tokens (if ever placed in URL fragment)
Threat
Authorization code in the redirect URL leaks via Referer header on subsequent navigation, browser history, screen-share, browser-sync, or third-party browser extension exfiltration
Likelihood
Medium (codes appear in the loopback URL by design)
Impact
Low for authorization code (single-use, PKCE-protected, immediately exchanged); Critical if access tokens were ever placed in URL
Mitigations
Skill never uses implicit grant or fragment-encoded tokens (Authorization Code only); loopback handler closes the browser tab via auto-redirect to a static "you may close this window" page after callback receipt, breaking the Referer chain; PKCE neutralizes leaked code value
Residual Risk
Low
Status
Mitigated
Source
RFC 6819 §4.4.2.5 (Threat: Authorization Code Leakage through Counterfeit Web Site); RFC 9700 §2.1.2 (avoid implicit grant); OWASP ASVS V51.4
Trust Boundary Crossed
Browser ↔ Skill Process (loopback)
Detection
Out of band (browser-history forensics); not directly detectable by the skill
Persisted access_token, refresh_token, client_secret in the on-disk credential cache
Threat
Another local user, container co-tenant, backup process, dotfile-syncer, or accidental git add reads the credential cache file from the user's home directory
Likelihood
Low on properly configured single-user workstations; Medium in shared dev hosts, Codespaces, and dotfile repositories
Impact
Critical (refresh token grants tokens until manual revocation; non-rotated per OA-11)
Mitigations
_check_credential_file_perms L530 enforces 0600 mode and refuses to load on permission widening; cache lock via _acquire_cache_lock L1121 prevents partial writes; cache path documented in skill SECURITY.md; .gitignore covers default cache locations; documented backup-exclusion guidance
Residual Risk
Low (file-system-level controls; OS account compromise defeats this mitigation)
Status
Mitigated
Source
RFC 9700 §2.6 (Token Storage and Handling); OWASP ASVS V8.2 (Client-Side Data Protection); MITRE ATT&CK T1555.003 (Credentials from Web Browsers: analog for cached tokens); CAPEC-509 (Kerberoasting: analog for cached credential theft)
Trust Boundary Crossed
Skill Process ↔ Token Cache File
Detection
Permission-mode self-check on every read (_check_credential_file_perms); audit-log file access via OS auditd / fs_usage if enabled
An attacker who exfiltrates the refresh_token (via OA-8 log leak, OA-10 file disclosure, OA-4 client_secret combined with stolen code, or out-of-band shoulder-surf) can obtain access tokens indefinitely until the user manually revokes the grant. Mural does NOT rotate refresh tokens despite their narrative documentation suggesting otherwise; verified via the response schema and the explicit "reuse" statement
Likelihood
Low (depends on a prior exfiltration vector landing successfully)
Impact
Critical (long-lived persistence; full delegated scope until manual revocation)
Mitigations
Refresh token covered by _REDACT_KEYS (OA-8 control); persisted only with 0600 mode (OA-10 control); skill SECURITY.md G-EOP-1 documents the Mural-account revocation runbook (https://app.mural.co/account/api); refresh code path _apply_refresh L1597 does not log the token value; consumers warned that refresh tokens are non-rotated and that revocation is the only invalidation path
Residual Risk
Medium (residual depends on user adherence to revocation runbook on suspected compromise; non-rotation is an upstream design decision the skill cannot change)
RFC 9700 §2.2.2 (Refresh Token Protection); RFC 6819 §5.2.2.3 (Refresh Token Rotation); Mural verbatim refresh-response schema: { "access_token": <TOKEN>, "expires_in": <EXPIRATION (in seconds)> } (no refresh_token field); Mural verbatim reference paragraph: "You can reuse your refresh_token as many times as you need to get a new access_token."
Trust Boundary Crossed
Skill Process ↔ Token Cache File; Skill Process ↔ Mural Token Endpoint
Detection
Mural-side anomaly detection on token-endpoint request frequency or geographic distribution; out-of-band review at https://app.mural.co/account/api
PKCE code_verifier (must remain secret to bind the code exchange)
Threat
Verifier leaks via log emission, weak entropy (predictable RNG), or insufficient length (fewer than 43 chars), allowing an attacker who also captured the code (OA-3 / OA-9) to exchange it
Likelihood
Low (skill uses secrets.token_urlsafe)
Impact
High if combined with a code interception
Mitigations
_generate_pkce_pair L1307 uses secrets.token_urlsafe(64) yielding 86 URL-safe characters (well above the RFC 7636 minimum of 43); _verify_pkce L1314 enforces S256 method (the only modern method, since Mural does not document PKCE method parameters the skill assumes S256 per RFC 7636 §4.2); verifier never logged (not in any log call site) and never persisted (in-process only)
Residual Risk
Very Low
Status
Mitigated
Source
RFC 7636 §4.1 (Code Verifier minimum entropy 256 bits, length 43–128); RFC 7636 §7.1 (Entropy of code_verifier); RFC 9700 §2.1.1 (PKCE for all OAuth clients); Mural verbatim PKCE acknowledgment: "we support PKCE (Proof Key for Code Exchange)"; note PKCE request/response parameters are NOT documented in Mural's parameter tables, so the skill implements per RFC 7636
Trust Boundary Crossed
In-process (verifier never crosses boundary except via TLS to token endpoint)
Detection
Token-exchange invalid_grant indicates verifier mismatch; entropy regression detected by unit test on _generate_pkce_pair
Mural authorization endpoint availability for this client / user
Threat
Buggy automation or attacker triggers repeated authorization requests (loopback handler crashes mid-flow, retried in a tight loop, or login storm), consuming Mural-side rate-limit budget and locking the user out
Likelihood
Low
Impact
Medium (skill unavailable until rate-limit window resets; user may need account-side intervention)
Mitigations
Single in-flight _run_login enforced by cache lock (_acquire_cache_lock L1121); exponential backoff on retryable errors; user-initiated only (no automatic re-login on every API call); documented login cadence guidance
Residual Risk
Low
Status
Mitigated
Source
RFC 6819 §5.1.5.2 (Threat: Denial of Service Attacks); OWASP ASVS V11 (Business Logic Verification)
Concurrent skill processes each detect the access token is expired and race to refresh; the resulting refresh storm hammers Mural's token endpoint and may produce inconsistent cached state
Likelihood
Low for single-user usage; Medium when the skill is invoked from multiple terminals or automation contexts simultaneously
Impact
Low to Medium (rate-limit penalty; brief unavailability)
Mitigations
Cache lock (_acquire_cache_lock L1121) serializes refresh; refresh attempt re-reads the cache after acquiring the lock to avoid duplicate refresh; access-token TTL of 900s (Mural verbatim "OAuth tokens expire after 15 minutes") sets refresh cadence; documented "do not script-loop the skill" guidance
Skill (or a future variant) requests broader scopes than required for the task at hand, or an attacker tampers with the scope parameter mid-flow to escalate; consent-phishing pattern is a recognized MITRE ATT&CK technique
Likelihood
Low (skill scope set is hardcoded and minimal)
Impact
High (excessive scope grants enable destructive operations or data exfiltration beyond the user's expected approval)
Mitigations
Scope is constructed from a hardcoded constant (not user-influenced); destructive operations require an explicit dispatch-time scope re-check (mural-skill-discipline /memories/repo/); least-privilege scope set documented in skill SECURITY.md; tag-level scopes (room:read, room:write) are space-delimited and case-sensitive per Mural's documented format
A bearer token (no client-binding) stolen via OA-8 / OA-10 / OA-11 can be replayed against any Mural API endpoint by any actor who possesses the token, with no cryptographic proof-of-possession required. RFC 9449 (DPoP) and FAPI 2.0 sender-constrained token profiles would mitigate this but Mural does not currently document support for either
Likelihood
Low (depends on a prior exfiltration vector)
Impact
High (full delegated scope until token expires; refresh token compounds the window per OA-11)
Mitigations
Defense in depth via OA-4 (client_secret protection), OA-8 (log redaction), OA-10 (file mode), OA-11 (revocation runbook); access-token TTL of 900s caps the post-theft replay window for the access token specifically; track Mural's roadmap for sender-constrained token support and adopt RFC 9449 DPoP if/when offered
Residual Risk
Medium (cannot be fully mitigated without upstream Mural support for sender-constrained tokens; this is an architectural limitation of bearer-token OAuth)
Mural-side anomaly detection on user-agent, IP, or request-pattern divergence
OA-17: Stolen-Token Abuse Window via Missing Rotation + Long Refresh TTL
Field
Value
Category
Elevation of Privilege
Asset
Compromise-recovery time (the window between token theft and effective revocation)
Threat
Because Mural does not rotate refresh tokens (OA-11) and does not document a refresh-token TTL, a stolen refresh token combined with absence of rotation means recovery requires the user to perform manual revocation at the Mural account UI. Until they do, the attacker retains the same authority as the legitimate user. This compounds the impact of any successful exfiltration vector
Likelihood
Low (compound event: requires successful exfiltration AND delayed user response)
Impact
Critical (open-ended persistence)
Mitigations
Documented incident-response runbook in skill SECURITY.md G-EOP-1 (Mural revocation URL: https://app.mural.co/account/api); access-token TTL of 900s caps the access-token-only attack window; client-side defenses against exfiltration (OA-4, OA-8, OA-10) reduce the precondition probability; advise consumers to monitor Mural account-side audit log on a routine cadence; track Mural's roadmap for refresh-token rotation support and adopt as soon as it is offered
Residual Risk
Medium (cannot be fully mitigated without upstream Mural support for refresh-token rotation; this is a documented Mural design limitation, not a skill defect)
Status
Partially Mitigated (architectural limitation documented; G-EOP-2 in skill SECURITY.md is now CONFIRMED CORRECT against Mural's published documentation)
Source
RFC 9700 §2.2.2 (Refresh Token Protection; recommends rotation); RFC 6819 §5.2.2.3 (Refresh Token Rotation); OAuth 2.1 §4.3.1; Mural verbatim refresh-response schema: { "access_token": <TOKEN>, "expires_in": <EXPIRATION (in seconds)> } (no refresh_token); Mural verbatim reuse statement: "You can reuse your refresh_token as many times as you need to get a new access_token."; Mural account-side revocation: https://app.mural.co/account/api
Trust Boundary Crossed
Skill Process ↔ Mural API; User ↔ Mural Account Console
Detection
Out-of-band Mural account-side audit; alert on token-issuance anomaly
The sbom-diff job in main.yml runs during each release to surface supply chain changes between consecutive versions. It compares the current dependency SBOM against the previous release, generating a structured dependency-diff.md report that is uploaded to the GitHub Release.
Field
Value
Trigger
Runs when release_created == 'true', after SBOM generation completes
Input
SPDX JSON dependency SBOMs from current build and previous GitHub Release
Output
dependency-diff.md uploaded to the GitHub Release as an asset
Failure Mode
continue-on-error: true prevents diff failures from blocking the release
Permissions
contents: write (release asset upload only)
The diff script parses SPDX JSON packages, excludes root document entries, and categorizes changes into three groups:
Added packages not present in the previous release
Removed packages no longer included in the current build
Version changes where the same package appears in both releases at different versions
When no previous release exists or the prior release lacks a dependency SBOM, the job exits cleanly without producing a diff. This graceful degradation ensures the first release in a repository proceeds without error.
HVE Core documents integrations with Model Context Protocol servers. This section analyzes the trust posture of each server.
NOTE
GitHub MCP is enabled by default in VS Code when using GitHub Copilot. The other servers are optional and recommended for an optimal HVE Core development experience. See MCP Configuration for setup instructions.
Follow-up items identified during the Phase 5 review of the Mural skill OAuth surface (tracked in .copilot-tracking/reviews/2026-05-10/mural-oauth-review.md):
_REDACT_KEYS allow-list and callsite audit completed in .copilot-tracking/reviews/2026-05-10/mural-redact-callsite-audit.md; all LOGGER.* and _emit callsites confirmed safe and code_challenge removed from the redact list (public by PKCE design).
Build an Atheris fuzz harness under .github/skills/experimental/mural/tests/fuzz/ exercising _redact() and _LoopbackHandler request parsing.
MURAL_KEYRING_BACKEND is a developer trust toggle: when set, the skill imports the named module via importlib and uses it as the OS keyring backend. Treat any value as code-execution surface; operators must only set it to a backend module they own or fully trust. Unset by default; on-disk cache (0600) is the production path.