HVE Core is an enterprise prompt engineering framework for GitHub Copilot consisting of:
- Markdown-based prompt artifacts (instructions, prompts, agents, skills)
- 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.
| Category | Status | Control Count | Automated |
|---|
| Supply Chain Security | Strong | 8 controls | 100% |
| Code Quality | Strong | 5 controls | 100% |
| Access Control | Strong | 4 controls | 100% |
| Vulnerability Management | Strong | 3 controls | 100% |
| Total | 20+ | 20 | 100% |
HVE Core contains five primary component categories:
-
Prompt Engineering Artifacts (.github/instructions/, .github/prompts/, .github/agents/, .github/skills/)
- Markdown files with YAML frontmatter
- Consumed by GitHub Copilot during development sessions
- No executable code execution within prompts
-
PowerShell Scripts (scripts/)
- Linting and validation utilities
- CI/CD automation support
- No external network connections except documented tool downloads
-
GitHub Actions Workflows (.github/workflows/)
- PR validation pipeline
- Security scanning (CodeQL, dependency review)
- Release automation
-
VS Code Extension (extension/)
- Packaging configuration
- Extension manifest
- No telemetry or data collection
-
Mural Skill Runtime (.github/skills/experimental/mural/)
flowchart TD
subgraph DEV["Developer Workstation"]
ARTIFACTS["HVE Core Artifacts<br/>(.instructions.md, .prompt.md, etc)"]
IDE["VS Code IDE"]
COPILOT["GitHub Copilot Extension"]
LOCALMCP["Local MCP Servers<br/>(optional)"]
SCRIPTS["Local Scripts<br/>(PowerShell)"]
DEVCON["Dev Container<br/>(optional)"]
end
subgraph GITHUB["GitHub Platform (Network Boundary)"]
LLMAPI["LLM API Service"]
REMOTEMCP["GitHub MCP Server"]
REPO["Repository"]
ACTIONS["GitHub Actions Runners"]
SCANNING["Security Scanning<br/>(CodeQL, Dep Review)"]
end
ARTIFACTS -->|"read into context"| COPILOT
IDE --> COPILOT
COPILOT -->|"prompts + context (HTTPS)"| LLMAPI
LLMAPI -->|"suggestions"| COPILOT
COPILOT <-->|"tool calls"| LOCALMCP
COPILOT <-->|"tool calls (HTTPS)"| REMOTEMCP
DEVCON -.->|"contains"| IDE
DEVCON -.->|"contains"| SCRIPTS
DEV -->|"git push"| REPO
REPO -->|"triggers"| ACTIONS
ACTIONS --> SCANNING
HVE Core artifacts are consumed by GitHub Copilot, which provides foundational security:
| Inherited Control | Provider | HVE Core Responsibility |
|---|
| LLM input/output filtering | GitHub Copilot | None; artifacts are Copilot inputs |
| Token encryption in transit | GitHub Copilot | None; handled by Copilot infrastructure |
| Organization policy enforcement | GitHub Copilot | Document compatible policy options |
| Audit logging | GitHub Copilot | None; uses Copilot audit streams |
| SOC 2 Type II compliance | GitHub | None; infrastructure control |
┌──────────────────────────────────────────────────────────────────────────────┐
│ TRUST BOUNDARY: Repository Contents │
│ ┌────────────────────────────────────────────────────────────────────────┐ │
│ │ Controlled Artifacts │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────────┐ │ │
│ │ │ Prompts │ │ Scripts │ │ Workflows │ │ Documentation │ │ │
│ │ │ .md files │ │ .ps1 files │ │ .yml files │ │ .md files │ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ └────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────────────────────────▼────────────────────────────────────┐ │
│ │ TRUST BOUNDARY: CI/CD Pipeline │ │
│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────────┐ │ │
│ │ │ PR Valid. │ │ CodeQL │ │ Dep Review │ │ Release │ │ │
│ │ │ Workflow │ │ Analysis │ │ Workflow │ │ Workflow │ │ │
│ │ └────────────┘ └────────────┘ └────────────┘ └────────────────┘ │ │
│ └────────────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────────┘
│
┌─────────────────────────────────┼──────────────────────────────────┐
│ ▼ │
│ TRUST BOUNDARY: External Dependencies │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │
│ │ npm │ │ GitHub │ │ PowerShell │ │ Third-party │ │
│ │ Packages │ │ Actions │ │ Gallery │ │ MCP Servers │ │
│ └────────────┘ └────────────┘ └────────────┘ └──────────────┘ │
└────────────────────────────────────────────────────────────────────┘
| Boundary | Assets Protected | Controls Enforced |
|---|
| Repository Contents | Source code, prompts, scripts | CODEOWNERS, branch protection, PR review |
| CI/CD Pipeline | Build artifacts, security scan results | Minimal permissions, dependency pinning |
| External Dependencies | npm packages, Actions, MCP servers | Dependency review, staleness monitoring |
| Dev Container | Development environment, tooling | SHA256 verification, first-party features |
| Mural Skill Runtime | OAuth tokens, Mural API egress | OS keyring / 0600 token cache, PKCE, loopback redirect URI |
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.
| Field | Value |
|---|
| Category | Spoofing |
| Asset | CI/CD pipeline integrity |
| Threat | Attacker compromises upstream Action repository and replaces tag with malicious code |
| Likelihood | Medium (documented supply chain attacks exist) |
| Impact | High (full CI/CD compromise, secret exfiltration) |
| Mitigations | Dependency pinning for all Actions, staleness monitoring, CodeQL scanning |
| Residual Risk | Low (SHA immutable; requires GitHub infrastructure compromise) |
| Status | Mitigated |
| Field | Value |
|---|
| Category | Spoofing |
| Asset | Build dependencies |
| Threat | Malicious package published with same name or typosquatting |
| Likelihood | Medium (common attack vector) |
| Impact | Medium (limited runtime exposure; primarily build-time) |
| Mitigations | Package-lock.json integrity, npm audit, dependency review |
| Residual Risk | Low |
| Status | Mitigated |
| Field | Value |
|---|
| Category | Tampering |
| Asset | Workflow files, security scripts |
| Threat | Attacker with write access disables security checks |
| Likelihood | Low (requires compromised maintainer account) |
| Impact | High (security controls bypassed) |
| Mitigations | CODEOWNERS enforcement, branch protection, PR review requirements |
| Residual Risk | Low |
| Status | Mitigated |
| Field | Value |
|---|
| Category | Tampering |
| Asset | Prompt artifacts |
| Threat | Contributor submits prompt with hidden malicious instructions |
| Likelihood | Medium (social engineering possible) |
| Impact | Medium (affects Copilot behavior for consumers) |
| Mitigations | PR review, CODEOWNERS, frontmatter validation |
| Residual Risk | Medium (semantic analysis not automated) |
| Status | Partially Mitigated |
| Field | Value |
|---|
| Category | Repudiation |
| Asset | Repository configuration |
| Threat | Admin makes security-impacting changes without audit trail |
| Likelihood | Low (GitHub provides audit logs) |
| Impact | Medium (accountability gap) |
| Mitigations | GitHub audit log, branch protection audit events |
| Residual Risk | Low |
| Status | Mitigated |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | Repository secrets, tokens |
| Threat | Secrets accidentally logged or included in build artifacts |
| Likelihood | Low (minimal secret usage) |
| Impact | High (credential compromise) |
| Mitigations | GitHub secret masking, GitHub secret scanning, minimal secret usage |
| Residual Risk | Low |
| Status | Mitigated |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | Prompt files, documentation |
| Threat | Internal URLs, API keys, or proprietary patterns exposed in prompts |
| Likelihood | Low (review process catches obvious cases) |
| Impact | Medium (information leakage) |
| Mitigations | PR review, GitHub secret scanning, documentation guidelines |
| Residual Risk | Low |
| Status | Mitigated |
| Field | Value |
|---|
| Category | Denial of Service |
| Asset | GitHub Actions minutes, runner availability |
| Threat | Malicious PR triggers expensive workflows repeatedly |
| Likelihood | Low (requires PR creation privileges) |
| Impact | Low (billing impact, temporary delays) |
| Mitigations | Workflow approval for first-time contributors, concurrency limits |
| Residual Risk | Low |
| Status | Mitigated |
| Field | Value |
|---|
| Category | Denial of Service |
| Asset | Build pipeline |
| Threat | Attacker publishes conflicting package preventing clean builds |
| Likelihood | Low |
| Impact | Medium (build failures) |
| Mitigations | Package-lock.json, scoped packages |
| Residual Risk | Low |
| Status | Mitigated |
| Field | Value |
|---|
| Category | Elevation of Privilege |
| Asset | GitHub Actions tokens |
| Threat | Compromised workflow step uses GITHUB_TOKEN beyond intended scope |
| Likelihood | Low (minimal permissions declared) |
| Impact | Medium (depends on token permissions) |
| Mitigations | Minimal permissions pattern, persist-credentials: false, inline comments on elevated permissions |
| Residual Risk | Low |
| Status | Mitigated with Accepted Risk |
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
| Field | Value |
|---|
| Category | Elevation of Privilege |
| Asset | Protected branches |
| Threat | Admin bypasses branch protection to merge unauthorized changes |
| Likelihood | Low (requires admin access and intentional bypass) |
| Impact | High (security controls circumvented) |
| Mitigations | Branch protection rules, audit logging, "Do not allow bypassing" |
| Residual Risk | Low |
| Status | Mitigated |
These threats address risks in the development container configuration used for Codespaces and local container development.
| Field | Value |
|---|
| Category | Spoofing |
| Asset | Dev container configuration |
| Threat | Malicious update to a feature version tag introduces compromised tooling |
| Likelihood | Low (first-party Microsoft features only) |
| Impact | Medium (development environment compromise) |
| Mitigations | First-party features only, PR review of devcontainer.json changes |
| Residual Risk | Low (Microsoft-maintained features with release controls) |
| Status | Mitigated |
| Field | Value |
|---|
| Category | Tampering |
| Asset | Container initialization scripts |
| Threat | Attacker modifies on-create.sh or post-create.sh to inject code |
| Likelihood | Low (requires PR approval, CODEOWNERS protection) |
| Impact | High (arbitrary code execution in dev environment) |
| Mitigations | CODEOWNERS, PR review, branch protection |
| Residual Risk | Low |
| Status | Mitigated |
| Field | Value |
|---|
| Category | Spoofing |
| Asset | External tools (gitleaks, shellcheck) |
| Threat | Compromised download source serves malicious binary |
| Likelihood | Very Low (SHA256 verification enforced) |
| Impact | High (malicious tooling in dev environment) |
| Mitigations | SHA256 checksum verification in on-create.sh |
| Residual Risk | Very Low (cryptographic verification prevents substitution) |
| Status | Mitigated |
These threats address risks specific to AI/ML systems as documented by OWASP LLM Top 10 and MITRE ATLAS.
| Field | Value |
|---|
| Category | LLM01: Prompt Injection (OWASP) |
| Asset | Copilot behavior, downstream code generation |
| Threat | Malicious instructions embedded in prompt artifacts manipulate Copilot |
| Likelihood | Medium |
| Impact | Medium (affects code generation quality and safety) |
| Mitigations | PR review, CODEOWNERS, clear artifact structure guidelines |
| Residual Risk | Medium (inherent to prompt-based systems) |
| Status | Partially Mitigated |
| Field | Value |
|---|
| Category | LLM02: Insecure Output Handling (OWASP) |
| Asset | Generated code |
| Threat | Copilot generates insecure code patterns based on prompt guidance |
| Likelihood | Medium |
| Impact | Variable (depends on consumer's review practices) |
| Mitigations | Security-focused prompts, consumer code review responsibility |
| Residual Risk | Medium (HVE Core provides guidance, not enforcement) |
| Status | Accepted with Documentation |
| Field | Value |
|---|
| Category | LLM03: Training Data Poisoning (OWASP) |
| Asset | Copilot model behavior |
| Threat | Malicious patterns in HVE Core influence Copilot training |
| Likelihood | Very Low (Copilot training controlled by GitHub) |
| Impact | Low (HVE Core is small input to large training corpus) |
| Mitigations | Out of scope; GitHub controls training pipeline |
| Residual Risk | Very Low |
| Status | Accepted (Outside Control) |
| Field | Value |
|---|
| Category | LLM04: Model Denial of Service (OWASP) |
| Asset | Copilot availability |
| Threat | Crafted prompts cause excessive resource consumption in Copilot |
| Likelihood | Very Low |
| Impact | Low (Copilot has rate limiting) |
| Mitigations | Copilot's built-in rate limiting and resource management |
| Residual Risk | Very Low |
| Status | Accepted (Outside Control) |
| Field | Value |
|---|
| Category | LLM05: Supply-Chain Vulnerabilities (OWASP) |
| Asset | MCP server integrations |
| Threat | Compromised MCP server provides malicious context to Copilot |
| Likelihood | Low (first-party servers) to Medium (third-party) |
| Impact | Medium (affects code generation context) |
| Mitigations | MCP server trust analysis, documentation of trust levels |
| Residual Risk | Low to Medium depending on server |
| Status | Mitigated with Documentation |
See MCP Server Trust Analysis for Mural-skill-specific OAuth credential and token-cache leakage controls.
| Field | Value |
|---|
| Category | LLM06: Sensitive Information Disclosure (OWASP) |
| Asset | User context, code patterns |
| Threat | Prompt artifacts cause Copilot to expose sensitive patterns |
| Likelihood | Low |
| Impact | Medium |
| Mitigations | Consumer responsibility; prompt guidelines discourage sensitive data |
| Residual Risk | Low |
| Status | Mitigated with Documentation |
| Field | Value |
|---|
| Category | LLM07: Insecure Plugin Design (OWASP) |
| Asset | MCP server integrations, VS Code extension |
| Threat | Extension or MCP server allows unauthorized operations |
| Likelihood | Low (extension has no sensitive operations) |
| Impact | Low to Medium |
| Mitigations | Minimal extension functionality, MCP server trust documentation |
| Residual Risk | Low |
| Status | Mitigated |
| Field | Value |
|---|
| Category | LLM08: Excessive Agency (OWASP) |
| Asset | Autonomous Copilot operations |
| Threat | Prompts grant Copilot excessive autonomous capabilities |
| Likelihood | Low (prompts are guidance, not permissions) |
| Impact | Variable |
| Mitigations | Copilot's built-in guardrails, tool confirmation dialogs |
| Residual Risk | Low |
| Status | Mitigated (Copilot Controls) |
| Field | Value |
|---|
| Category | LLM09: Overreliance (OWASP) |
| Asset | Code quality, developer decision-making |
| Threat | Developers accept Copilot output without verification |
| Likelihood | Medium |
| Impact | Variable (depends on context) |
| Mitigations | Documentation emphasizing review, security-focused prompts |
| Residual Risk | Medium (behavioral, not technical) |
| Status | Accepted with Documentation |
| Field | Value |
|---|
| Category | LLM10: Model Theft (OWASP) |
| Asset | N/A |
| Threat | HVE Core does not host or distribute models |
| Likelihood | N/A |
| Impact | N/A |
| Mitigations | N/A |
| Residual Risk | N/A |
| Status | Not Applicable |
| Field | Value |
|---|
| Category | MITRE ATLAS AML.T0043 |
| Asset | Prompt artifacts |
| Threat | Adversary crafts prompt content to cause model misbehavior |
| Likelihood | Medium |
| Impact | Medium |
| Mitigations | PR review process, CODEOWNERS, artifact structure validation |
| Residual Risk | Medium |
| Status | Partially Mitigated |
| Field | Value |
|---|
| Category | MITRE ATLAS AML.T0048 |
| Asset | Security recommendations in prompts |
| Threat | Prompts designed to cause Copilot to bypass security guidance |
| Likelihood | Low |
| Impact | Medium |
| Mitigations | Security-first prompt design principles, review process |
| Residual Risk | Low |
| Status | Mitigated |
For runtime supply-chain posture of locally executed MCP servers, see the MCP Server Trust Analysis runtime trust table.
These threats address ethical and responsible AI considerations aligned with Microsoft's Responsible AI principles.
| Field | Value |
|---|
| Category | Fairness (Responsible AI) |
| Asset | Generated code quality across contexts |
| Threat | Prompts inadvertently favor certain coding styles or exclude accessibility |
| Likelihood | Medium |
| Impact | Medium (affects inclusivity of generated code) |
| Mitigations | Inclusive language guidelines, accessibility-aware prompts |
| Residual Risk | Medium |
| Status | Partially Mitigated |
| Field | Value |
|---|
| Category | Reliability & Safety (Responsible AI) |
| Asset | Prompt consistency |
| Threat | Same prompt produces significantly different outputs |
| Likelihood | Medium (inherent to LLMs) |
| Impact | Low to Medium |
| Mitigations | Structured prompts, explicit instructions, testing guidance |
| Residual Risk | Medium (LLM behavior inherently variable) |
| Status | Accepted with Documentation |
| Field | Value |
|---|
| Category | Privacy & Security (Responsible AI) |
| Asset | Developer context, code patterns |
| Threat | Prompts cause Copilot to surface or infer private information |
| Likelihood | Low |
| Impact | Medium |
| Mitigations | Privacy-conscious prompt design, consumer guidelines |
| Residual Risk | Low |
| Status | Mitigated with Documentation |
| Field | Value |
|---|
| Category | Privacy & Security (Responsible AI) |
| Asset | Meeting transcripts, customer confidential data, PII |
| Threat | 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 |
| Field | Value |
|---|
| Category | Inclusiveness (Responsible AI) |
| Asset | Prompt artifacts, documentation |
| Threat | Language in prompts excludes or marginalizes user groups |
| Likelihood | Low (writing style guidelines address this) |
| Impact | Medium (affects adoption and trust) |
| Mitigations | Inclusive writing guidelines, spell check, PR review |
| Residual Risk | Low |
| Status | Mitigated |
| Field | Value |
|---|
| Category | Transparency (Responsible AI) |
| Asset | User understanding of system behavior |
| Threat | Prompts cause unexpected Copilot behavior not explained to users |
| Likelihood | Medium |
| Impact | Low to Medium |
| Mitigations | Clear documentation, explicit prompt descriptions in frontmatter |
| Residual Risk | Low |
| Status | Mitigated |
| Field | Value |
|---|
| Category | Accountability (Responsible AI) |
| Asset | Liability and responsibility clarity |
| Threat | Ambiguity about who is responsible for Copilot-generated code issues |
| Likelihood | Medium (common confusion) |
| Impact | Medium |
| Mitigations | Documentation clarifying HVE Core provides guidance only |
| Residual Risk | Low |
| Status | Mitigated with Documentation |
| Field | Value |
|---|
| Category | Human Oversight (Responsible AI) |
| Asset | Code quality, security |
| Threat | Prompts encourage accepting Copilot suggestions without review |
| Likelihood | Low (prompts emphasize review) |
| Impact | Variable |
| Mitigations | Prompts include review reminders, security-conscious patterns |
| Residual Risk | Low |
| Status | Mitigated |
| Field | Value |
|---|
| Category | Value Alignment (Responsible AI) |
| Asset | Organizational trust |
| Threat | Prompt artifacts conflict with consumer organization's values |
| Likelihood | Low |
| Impact | Medium (reputational) |
| Mitigations | General-purpose prompts, customization guidance for consumers |
| Residual Risk | Low |
| Status | Mitigated with Documentation |
| Field | Value |
|---|
| Category | Proportionality (Responsible AI) |
| Asset | Developer autonomy |
| Threat | Prompts push Copilot toward excessive automation reducing human judgment |
| Likelihood | Low |
| Impact | Medium |
| Mitigations | Human-in-the-loop design patterns in prompts |
| Residual Risk | Low |
| Status | Mitigated |
| Field | Value |
|---|
| Category | Contestability (Responsible AI) |
| Asset | User agency |
| Threat | Users cannot override or question Copilot behavior influenced by prompts |
| Likelihood | Low (Copilot suggestions are optional) |
| Impact | Low |
| Mitigations | Copilot's non-mandatory nature, edit/reject options built-in |
| Residual Risk | Very Low |
| Status | Mitigated (Copilot Controls) |
| Field | Value |
|---|
| Category | Societal Impact (Responsible AI) |
| Asset | Developer skill development |
| Threat | Over-reliance on AI-assisted coding reduces skill development |
| Likelihood | Medium (industry-wide concern) |
| Impact | Low for HVE Core specifically |
| Mitigations | Prompts emphasize learning and understanding, not just output |
| Residual Risk | Medium (societal, not technical) |
| Status | Accepted with Documentation |
| Field | Value |
|---|
| Category | Environmental Impact (Responsible AI) |
| Asset | Compute resources |
| Threat | Inefficient prompts cause unnecessary model computation |
| Likelihood | Low |
| Impact | Low (marginal compute impact) |
| Mitigations | Efficient prompt design guidelines |
| Residual Risk | Very Low |
| Status | Accepted |
| Field | Value |
|---|
| Category | Misinformation (Responsible AI) |
| Asset | Documentation and code accuracy |
| Threat | Prompts cause Copilot to generate plausible but incorrect content |
| Likelihood | Medium (LLM hallucination is known issue) |
| Impact | Medium |
| Mitigations | Verification prompts, citation requirements in prompt guidelines |
| Residual Risk | Medium (inherent LLM limitation) |
| Status | Partially Mitigated |
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).
The verbatim quotes and validation log are recorded in .copilot-tracking/research/2026-05-10/oauth-stride-threat-model-validation-research.md.
External standards are cited inline.
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.
| 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) |
| Status | Mitigated with Documentation |
| Source | RFC 6819 §4.1.4 (Threat: End-User Credentials Phished); MITRE ATT&CK T1539 (Steal Web Session Cookie); Mural authorization endpoint verbatim: "Authorization URL: https://app.mural.co/api/public/v1/authorization/oauth2/" |
| Trust Boundary Crossed | Browser ↔ Mural Authorization Server |
| Detection | Out of band (Mural account-side anomaly review at https://app.mural.co/account/api); the local skill cannot detect this |
| 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 |
| Status | Mitigated by Design (single-AS skill) |
| Source | RFC 9207 §1 (OAuth 2.0 Authorization Server Issuer Identification); RFC 9700 §4.4 (AS Mix-Up); Mural callback verified to expose code + state only (no iss): "https://cleverexample.com/oauth/callback?code=:code&state=:state" |
| Trust Boundary Crossed | Browser ↔ Mural Authorization Server; Skill Process ↔ Mural Token Endpoint |
| Detection | Cross-AS code rejection logged at the wrong AS's token endpoint (invalid_grant or invalid_client); audit AS-side for unexpected token requests |
| Field | Value |
|---|
| Category | Spoofing |
| Asset | 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) |
| Status | Mitigated |
| Source | RFC 8252 §7.3 (Loopback Interface Redirection); RFC 7636 §1 (PKCE motivation: authorization code interception attack); CAPEC-21 (Exploitation of Trusted Identifiers) |
| Trust Boundary Crossed | Browser ↔ Skill Process (loopback) |
| Detection | EADDRINUSE on bind; loopback handler logs unexpected callbacks; second invalid_grant ("already used") on token exchange attempt |
| 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 |
| Detection | File-mode audit (_check_credential_file_perms); gitleaks pre-commit; CodeQL secret-pattern scanning; Mural-side anomaly detection on token-request volume |
| Field | Value |
|---|
| Category | Tampering |
| Asset | 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) |
| Field | Value |
|---|
| Category | Tampering |
| Asset | One-time-use guarantee on the authorization code |
| Threat | 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 |
| Field | Value |
|---|
| Category | Repudiation |
| Asset | OAuth event audit log (login, refresh, revoke, scope grant) |
| Threat | 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 |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | access_token, refresh_token, client_secret, code, code_verifier, future id_token / assertion / client_assertion / device_code / password |
| Threat | 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 |
| 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 |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | 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 |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | refresh_token issued by Mural |
| Threat | 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) |
| Status | Partially Mitigated (Mural-side limitation documented; client-side controls maximized) |
| Source | 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 |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | 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 |
| Field | Value |
|---|
| Category | Denial of Service |
| Asset | 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) |
| Trust Boundary Crossed | Skill Process ↔ Mural Authorization Server |
| Detection | HTTP 429 from Mural; cache-lock contention metric |
| Field | Value |
|---|
| Category | Denial of Service |
| Asset | Mural token endpoint availability; cached token consistency across concurrent skill invocations |
| Threat | 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 |
| Residual Risk | Low |
| Status | Mitigated |
| Source | RFC 9700 §2.2.2; Mural verbatim: "By default, OAuth tokens expire after 15 minutes" |
| Trust Boundary Crossed | Skill Process ↔ Mural Token Endpoint |
| Detection | HTTP 429 from token endpoint; cache-lock wait-time metric |
| Field | Value |
|---|
| Category | Elevation of Privilege |
| Asset | Granted OAuth scope set |
| Threat | 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 |
| Residual Risk | Low |
| Status | Mitigated |
| Source | MITRE ATT&CK T1528 (Steal Application Access Token); CAPEC-593 (Session Hijacking); RFC 6819 §5.1.5.1 (Threat: Obtaining Tokens with Wrong Scope); OWASP ASVS V51.2.1 (least-privilege scope) |
| Trust Boundary Crossed | Browser ↔ Mural Authorization Server |
| Detection | Scope diff between requested and granted (if Mural ever emits granted scope in token response); periodic Mural-side scope audit at https://app.mural.co/account/api |
| Field | Value |
|---|
| Category | Elevation of Privilege |
| Asset | Bearer access_token issued by Mural |
| Threat | 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) |
| Status | Partially Mitigated (architectural limitation documented) |
| Source | RFC 9449 (OAuth 2.0 Demonstrating Proof of Possession (DPoP)); FAPI 2.0 Security Profile §5.3 (sender-constrained access tokens); RFC 9700 §2.2.1 (Token Replay Prevention); MITRE ATT&CK T1550.001 (Application Access Token); CAPEC-593 |
| Trust Boundary Crossed | Skill Process ↔ Mural API |
| Detection | Mural-side anomaly detection on user-agent, IP, or request-pattern divergence |
| 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 |
These threats address credential and error-handling risks specific to the Jira skill (.github/skills/jira/jira/scripts/jira.py), a single-file standard-library CLI that authenticates to a Jira instance with a PAT (Authorization: Bearer) or Jira Cloud Basic auth (base64(email:token)) read from the environment per invocation.
The catalog uses the same extended 11-row format as the OAuth threats. The authoritative per-skill model is the Jira skill SECURITY.md.
Residual redaction-architecture hardening (central _emit() sink, LOGGER, typed JiraAPIError, source-contract redaction tests) is tracked on issues #1555, #1556, and #1559.
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | Jira PAT (Authorization: Bearer) |
| Threat | A raw exception or diagnostic that embeds the request URL, headers, or upstream error body could surface the bearer token in stderr, an audit record, or a captured log |
| Likelihood | Low |
| Impact | High (token grants the operator's Jira scope until revoked) |
| Mitigations | Token sent only in the Authorization header over TLS and never logged; remote error text passes through _redact_sensitive_text (masks Bearer/Basic, Authorization, query-string secrets) before display; ScriptError messages are built from structured fields, not raw responses. Central _emit()/LOGGER sink and typed JiraAPIError tracked on #1555/#1556 |
| Residual Risk | Low |
| Status | Partially Mitigated (redaction in place; central sink pending #1555/#1556) |
| Source | CWE-532 (Insertion of Sensitive Information into Log File); OWASP ASVS v4 §7.1.1 |
| Trust Boundary Crossed | Skill Process ↔ Operator diagnostics / audit sink |
| Detection | Source-contract redaction tests planned (#1559); code review |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | Jira Cloud Basic credential (base64(email:token)) |
| Threat | The Basic Authorization value is reversible base64; if it reaches a log or error string it can be trivially decoded to recover the email and API token |
| Likelihood | Low |
| Impact | High |
| Mitigations | _redact_sensitive_text masks the Basic credential in error text; the credential is built from ASCII-validated components and used only as an in-transit header over TLS; never persisted |
| Residual Risk | Low |
| Status | Partially Mitigated (#1556) |
| Source | CWE-522 (Insufficiently Protected Credentials); RFC 7617 (Basic auth is base64, not encryption) |
| Trust Boundary Crossed | Skill Process ↔ Operator diagnostics |
| Detection | Redaction contract tests (#1559) |
| Field | Value |
|---|
| Category | Spoofing / Tampering |
| Asset | Request destination integrity (where the bearer token is sent) |
| Threat | A crafted JIRA_BASE_URL (embedded userinfo, alternate host, redirect chain) could retarget authenticated requests to an attacker-controlled origin, leaking the token |
| Likelihood | Low |
| Impact | High |
| Mitigations | _canonicalize_base_url reduces the value to an origin-only URL and rejects control characters, userinfo, query, fragment, and non-root paths; HTTPS enforced for non-loopback hosts; _NoRedirect opener refuses 30x so the token is never replayed cross-host |
| Residual Risk | Low |
| Status | Mitigated |
| Source | CWE-918 (Server-Side Request Forgery); OWASP ASVS v4 §12.6 |
| Trust Boundary Crossed | Skill Process ↔ Jira Instance (network) |
| Detection | Transport regression tests (redirect blocking, HTTPS guard) |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | Diagnostic output integrity |
| Threat | A hostile or misconfigured Jira response could embed secrets or sensitive content that, if echoed verbatim, leaks to stderr or downstream automation |
| Likelihood | Low |
| Impact | Medium |
| Mitigations | Error bodies are JSON-parsed first and only redacted for presentation (_extract_error_message → _redact_sensitive_text); responses are read through a MAX_BODY_BYTES-capped reader with JSON content-type fail-closed |
| Residual Risk | Low |
| Status | Mitigated |
| Source | CWE-209 (Generation of Error Message Containing Sensitive Information) |
| Trust Boundary Crossed | Jira Instance ↔ Skill Process ↔ Operator diagnostics |
| Detection | Error-redaction regression tests |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | Jira PAT / API token in the process environment |
| Threat | A future debug path that dumps os.environ (or a traceback that captured it) could expose JIRA_PAT / JIRA_API_TOKEN |
| Likelihood | Low |
| Impact | High |
| Mitigations | No code path prints os.environ; credentials are read once and held on a frozen dataclass; unexpected exceptions surface a standard traceback that does not include variable values. A gated, redaction-wrapped debug traceback (_emit_debug_traceback) is tracked on #1555 |
| Residual Risk | Low |
| Status | Partially Mitigated (no env dump today; hardened debug path pending #1555) |
| Source | CWE-526 (Exposure of Sensitive Information Through Environment Variables); CWE-532 |
| Trust Boundary Crossed | Skill Process ↔ Operator diagnostics |
| Detection | Code review; redaction contract tests (#1559) |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | Authorization header value (Bearer / Basic) |
| Threat | JiraClient is a @dataclass(frozen=True) whose auth_header field holds the raw credential; the auto-generated repr() would expose it if the object were ever printed, logged, or captured by a debugger or traceback |
| Likelihood | Very Low (no current code path calls repr(client)) |
| Impact | High |
| Mitigations | No code path calls repr() on or logs the client; token used only as an in-transit header. A __repr__ / field-mask override is tracked on #1555/#1556 |
| Residual Risk | Low |
| Status | Open (latent gap; no repr override); tracked on #1555/#1556 |
| Source | CWE-215 (Insertion of Sensitive Information Into Debugging Code); CWE-532 |
| Trust Boundary Crossed | Skill Process ↔ Operator diagnostics |
| Detection | repr() negative test planned (#1559) |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | Operator-supplied stdin comment payload |
| Threat | handle_comment reads a comment body from stdin; if that payload (which may contain sensitive text) were embedded in an error message or traceback, it could leak |
| Likelihood | Low |
| Impact | Low |
| Mitigations | stdin is read through _read_stdin(MAX_BODY_BYTES) (size-capped); parse failures raise ScriptError with a static message, not the raw payload; error text is redacted before display |
| Residual Risk | Low |
| Status | Mitigated |
| Source | CWE-209; CWE-117 (Improper Output Neutralization for Logs) |
| Trust Boundary Crossed | CLI caller ↔ Skill Process |
| Detection | Redaction contract tests (#1559) |
These threats address credential and error-handling risks specific to the GitLab skill (.github/skills/gitlab/gitlab/scripts/gitlab.py), a single-file standard-library CLI that authenticates with a PAT (PRIVATE-TOKEN header) read from GITLAB_TOKEN and resolves the project from a read-only git remote subprocess.
The authoritative per-skill model is the GitLab skill SECURITY.md.
Residual redaction-architecture hardening (central _emit() sink, LOGGER, typed GitLabAPIError replacing die(), single-urlopen refactor, source-contract tests) is tracked on issues #1555, #1557, and #1559.
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | GitLab PAT (PRIVATE-TOKEN header) |
| Threat | A raw exception, die() message, or diagnostic embedding the URL, headers, or upstream body could surface the token |
| Likelihood | Low |
| Impact | High |
| Mitigations | Token sent only in PRIVATE-TOKEN over TLS and never logged; error and non-JSON text passes through _redact (masks PRIVATE-TOKEN, Authorization, cookies, query-string secrets) before display. Central _emit()/LOGGER sink and typed GitLabAPIError (replacing die()) tracked on #1555/#1557 |
| Residual Risk | Low |
| Status | Partially Mitigated (redaction in place; central sink + typed error pending #1555/#1557) |
| Source | CWE-532; OWASP ASVS v4 §7.1.1 |
| Trust Boundary Crossed | Skill Process ↔ Operator diagnostics |
| Detection | Redaction contract tests (#1559) |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | gitlab_token PAT held in mutable module-global state |
| Threat | The token is stored in a module-level global; a globals() dump, repr, or debugger frame could expose it |
| Likelihood | Very Low |
| Impact | High |
| Mitigations | No code path dumps globals; token used only as the PRIVATE-TOKEN header. Moving the credential onto an immutable config object (mirroring Jira's dataclass) is tracked on #1555/#1557 |
| Residual Risk | Low |
| Status | Open (module-global remains); tracked on #1555/#1557 |
| Source | CWE-526; CWE-215 |
| Trust Boundary Crossed | Skill Process ↔ Operator diagnostics |
| Detection | Code review; contract tests (#1559) |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | CI job-trace egress and PRIVATE-TOKEN |
| Threat | cmd_job_log issues a second HTTP request outside the central request() helper; historically this bypassed transport hardening and printed the raw error body |
| Likelihood | Low |
| Impact | Medium |
| Mitigations | The second request now routes through the shared _NoRedirect _OPENER with REQUEST_TIMEOUT and MAX_BODY_BYTES; job-trace output passes through _redact and is truncated via _preview_text. Collapsing it into a single request() call site (source-contract single-urlopen test) is tracked on #1555/#1557 |
| Residual Risk | Low |
| Status | Partially Mitigated (egress redacted + hardened opener; single-urlopen refactor pending #1555/#1557) |
| Source | CWE-532; CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor) |
| Trust Boundary Crossed | GitLab Instance ↔ Skill Process ↔ Operator diagnostics |
| Detection | Single-urlopen source-contract test (#1559) |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | Diagnostic output integrity |
| Threat | The die() helper prints an error string and exits; if callers pass a raw upstream body, secrets or sensitive content could leak |
| Likelihood | Low |
| Impact | Medium |
| Mitigations | Error bodies are parsed then redacted before reaching die(); non-JSON bodies pass through _redact + _preview_text. Replacing die() with a typed GitLabAPIError carrying only structured fields is tracked on #1555/#1557 |
| Residual Risk | Low |
| Status | Partially Mitigated (#1555/#1557) |
| Source | CWE-209 |
| Trust Boundary Crossed | Skill Process ↔ Operator diagnostics |
| Detection | Redaction contract tests (#1559) |
| Field | Value |
|---|
| Category | Spoofing / Tampering |
| Asset | Request destination integrity |
| Threat | A crafted GITLAB_URL could retarget authenticated requests to an attacker origin and leak the token |
| Likelihood | Low |
| Impact | High |
| Mitigations | _normalize_base_url reduces to origin-only and rejects userinfo, query, fragment, non-root paths, and control characters; HTTPS enforced off-loopback; _NoRedirect refuses 30x |
| Residual Risk | Low |
| Status | Mitigated |
| Source | CWE-918; OWASP ASVS v4 §12.6 |
| Trust Boundary Crossed | Skill Process ↔ GitLab Instance |
| Detection | Transport regression tests |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | Diagnostic output integrity |
| Threat | A hostile or misconfigured GitLab response or CI trace could embed secrets that leak if echoed verbatim |
| Likelihood | Medium (CI traces routinely contain secret-shaped content) |
| Impact | Medium |
| Mitigations | Error bodies parsed-then-redacted; job traces emitted through _redact with truncation; JSON content-type fail-closed; MAX_BODY_BYTES cap |
| Residual Risk | Low |
| Status | Mitigated |
| Source | CWE-209; CWE-200 |
| Trust Boundary Crossed | GitLab Instance ↔ Skill Process ↔ Operator diagnostics |
| Detection | Redaction regression tests |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | GITLAB_TOKEN in the process environment |
| Threat | A future debug path dumping os.environ could expose the token |
| Likelihood | Low |
| Impact | High |
| Mitigations | No code path prints os.environ; token read once into module state. Gated redaction-wrapped debug traceback tracked on #1555/#1557 |
| Residual Risk | Low |
| Status | Partially Mitigated (no env dump today; hardened debug path pending #1555/#1557) |
| Source | CWE-526; CWE-532 |
| Trust Boundary Crossed | Skill Process ↔ Operator diagnostics |
| Detection | Code review; contract tests (#1559) |
These threats address credential and content-egress risks specific to the tts-voiceover skill (.github/skills/experimental/tts-voiceover/scripts/), a Python CLI that escapes speaker-notes text into SSML, synthesizes audio through the Azure Cognitive Services Speech SDK over TLS, and embeds the audio into a PowerPoint deck.
It authenticates with a SPEECH_KEY subscription key or an Entra token minted by DefaultAzureCredential. The authoritative per-skill model is the tts-voiceover skill SECURITY.md.
Its headline residual is speaker-notes content egress to the Azure region; input-parser defense-in-depth is tracked on #1056 / PR #1695.
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | SPEECH_KEY Azure Speech subscription key |
| Threat | An exception or diagnostic embedding the key or SDK request context could surface it in stderr or a log |
| Likelihood | Low |
| Impact | High (key grants synthesis on the subscription until rotated) |
| Mitigations | Key read per invocation and passed only to the Speech SDK over TLS; never persisted and never logged; the SDK owns transport and does not echo the key |
| Residual Risk | Low |
| Status | Mitigated |
| Source | CWE-532; OWASP ASVS v4 §7.1.1 |
| Trust Boundary Crossed | Skill Process ↔ Azure Speech; Skill Process ↔ Operator diagnostics |
| Detection | Code review |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | Entra access token (aad#{resource}#{token}) |
| Threat | The composed aad#{resource_id}#{token} authorization value could leak via a debug print or captured traceback |
| Likelihood | Low |
| Impact | High (bearer token for cognitiveservices.azure.com until expiry) |
| Mitigations | Token minted per invocation by DefaultAzureCredential, refreshed near expiry, and passed only to the SDK; not logged or persisted; short TTL caps the exposure window |
| Residual Risk | Low |
| Status | Mitigated |
| Source | CWE-532; CWE-522 |
| Trust Boundary Crossed | Skill Process ↔ Entra / Azure Speech |
| Detection | Code review |
| Field | Value |
|---|
| Category | Information Disclosure |
| Asset | Speaker-notes content (content.yaml) |
| Threat | All narration text leaves the trust boundary to the configured Azure Speech region for synthesis; confidential content could egress without a classification or consent gate |
| Likelihood | Medium (any run sends content off-box) |
| Impact | Medium (depends on content sensitivity) |
| Mitigations | Egress is over TLS to the operator-configured region only; documented as the primary residual in the skill SECURITY.md; operators control what content is supplied and which region is used. No automated data-classification gate exists (documented gap) |
| Residual Risk | Medium |
| Status | Partially Mitigated (documented; no automated classification gate) |
| Source | CWE-200 (Exposure of Sensitive Information to an Unauthorized Actor) |
| Trust Boundary Crossed | Operator Workstation ↔ Azure Speech (region) |
| Detection | Out-of-band Azure-side monitoring; operator review of inputs |
| Field | Value |
|---|
| Category | Tampering |
| Asset | SSML synthesis request integrity |
| Threat | Speaker-notes text interpolated into SSML could inject markup that alters synthesis or smuggles control elements |
| Likelihood | Low |
| Impact | Low |
| Mitigations | Speaker notes are XML-escaped / quoteattr-quoted before insertion into SSML |
| Residual Risk | Low |
| Status | Mitigated |
| Source | CWE-91 (XML Injection); CWE-116 (Improper Encoding or Escaping of Output) |
| Trust Boundary Crossed | Inputs ↔ Skill Process ↔ Azure Speech |
| Detection | Unit tests on SSML escaping |
| Field | Value |
|---|
| Category | Tampering / Elevation of Privilege |
| Asset | Host process integrity |
| Threat | Malicious content.yaml or input PPTX could exploit unsafe YAML deserialization or XML external-entity resolution during parsing |
| Likelihood | Low |
| Impact | Medium |
| Mitigations | yaml.safe_load for YAML; python-pptx OOXML parsing with external-entity resolution disabled; the single raw lxml parse targets a hardcoded trusted timing-template constant (not attacker-influenced) and is being hardened as defense-in-depth (#1056 / PR #1695) |
| Residual Risk | Low |
| Status | Partially Mitigated (safe parsers in place; lxml defense-in-depth pending #1056/#1695) |
| Source | CWE-611 (Improper Restriction of XML External Entity Reference); CWE-502 (Deserialization of Untrusted Data) |
| Trust Boundary Crossed | Inputs ↔ Skill Process |
| Detection | Parser hardening tracked on #1056/#1695 |
| Field | Value |
|---|
| Category | Elevation of Privilege / Spoofing |
| Asset | Ambient Azure identity used for synthesis |
| Threat | DefaultAzureCredential walks a broad chain (environment, managed identity, Azure CLI, and more); on a shared host it could resolve to an unintended, more-privileged identity than the operator expects |
| Likelihood | Low |
| Impact | Medium |
| Mitigations | Token scoped to cognitiveservices.azure.com/.default; credential resolution is per-invocation and non-persistent; operators can pin the identity via the environment. Chain breadth documented as a residual in the skill SECURITY.md |
| Residual Risk | Medium |
| Status | Partially Mitigated (documented; chain breadth inherent to DefaultAzureCredential) |
| Source | CWE-269 (Improper Privilege Management) |
| Trust Boundary Crossed | Skill Process ↔ Entra |
| Detection | Operator review of the resolved identity |
| Field | Value |
|---|
| Category | Spoofing / Tampering |
| Asset | Synthesis request destination (where content and credential are sent) |
| Threat | A tampered region or endpoint configuration could direct content and the credential to an attacker-controlled endpoint |
| Likelihood | Low |
| Impact | High |
| Mitigations | The endpoint is derived from the operator-supplied region and reached over TLS via the SDK (system trust store); credentials and content are sent only to the configured Azure endpoint |
| Residual Risk | Low |
| Status | Mitigated (TLS; operator-controlled region) |
| Source | CWE-918; CWE-297 (Improper Validation of Certificate with Host Mismatch, mitigated by SDK TLS) |
| Trust Boundary Crossed | Skill Process ↔ Azure Speech |
| Detection | TLS validation via the SDK |
| ID | Control | Implementation | Validates Against |
|---|
| SC-1 | Dependency Pinning Validation | Test-DependencyPinning.ps1 | S-1, S-2 |
| SC-2 | SHA Staleness Monitoring | Test-SHAStaleness.ps1 | S-1 |
| SC-3 | Dependency Review | dependency-review.yml | S-2, AI-5 |
| SC-4 | npm Security Audit | npm audit in pr-validation.yml | S-2 |
| SC-5 | Dependabot Updates | dependabot.yml | S-1, S-2 |
| SC-6 | Tool Checksum Verification | scripts/security/tool-checksums.json | S-1 |
| SC-7 | SBOM Generation and Attestation | anchore/sbom-action, actions/attest in main.yml | S-1, S-2 |
| SC-8 | SBOM Dependency Diff | sbom-diff job in main.yml | S-1, S-2 |
| SC-9 | VEX Vulnerability Triage and Attestation | vex-detect.yml, vex-draft.md, attest-and-upload-vex in release-stable.yml | S-1, S-2 |
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.
| ID | Control | Implementation | Validates Against |
|---|
| CQ-1 | CodeQL Analysis | codeql-analysis.yml | T-1, E-1 |
| CQ-2 | Markdown Linting | lint:md npm script | T-2, RAI-4 |
| CQ-3 | Frontmatter Validation | Validate-MarkdownFrontmatter.ps1 | T-2 |
| CQ-4 | PowerShell Analysis | Invoke-PSScriptAnalyzer.ps1 | T-1 |
| CQ-5 | YAML Linting | Invoke-YamlLint.ps1 | T-1 |
| ID | Control | Implementation | Validates Against |
|---|
| AC-1 | Branch Protection | Repository settings | T-1, E-2 |
| AC-2 | CODEOWNERS Enforcement | .github/CODEOWNERS | T-1, T-2 |
| AC-3 | PR Review Requirements | Branch protection rules | T-2, AI-1 |
| AC-4 | Minimal Workflow Permissions | permissions: in all workflows | E-1 |
| ID | Control | Implementation | Validates Against |
|---|
| VM-1 | Coordinated Disclosure | SECURITY.md | I-1 |
| VM-2 | Secret Scanning | GitHub native, gitleaks PR gate (gitleaks-scan.yml) | I-1, I-2 |
| VM-3 | Credential Persistence Disabled | persist-credentials: false | I-1, E-1 |
This section presents the security assurance case using Goal Structuring Notation (GSN) patterns.
G0: HVE Core is acceptably secure for its intended use as an enterprise prompt engineering framework.
| Goal | Statement | Strategy |
|---|
| G1 | Supply chain attacks are mitigated | S1: Defense-in-depth controls |
| G2 | Unauthorized modifications are prevented | S2: Access control enforcement |
| G3 | AI-specific risks are documented and addressed | S3: Risk acceptance with documentation |
| G4 | Responsible AI principles are followed | S4: Guidelines and review processes |
| Goal | Evidence |
|---|
| G1 | Dependency pinning logs, staleness reports, dependency review results, SBOM attestation verification, dependency SBOM diff reports |
| G2 | Branch protection configuration, CODEOWNERS file, PR review history |
| G3 | This security model document, OAuth Authentication Threats, MCP Server Trust Analysis |
| G4 | Writing style guidelines, inclusive language checks, PR reviews |
| ID | Assumption | Justification |
|---|
| A1 | GitHub platform security is adequate | SOC 2 Type II certified |
| A2 | GitHub Copilot provides baseline AI safety | Microsoft RAI compliance |
| A3 | Contributors act in good faith | PR review provides verification |
| A4 | Consumers implement their own code review | Documented as consumer responsibility |
HVE Core achieves acceptable security through:
- Automated Controls: 20+ security controls execute automatically via CI/CD
- Defense-in-Depth: Multiple overlapping controls for critical threats
- Transparent Risk Acceptance: AI-inherent risks documented with clear boundaries
- Inherited Security: Uses GitHub and Copilot platform security
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.
| Server | Provider | Classification | Trust Level | Data Flow Risk | Default |
|---|
| GitHub MCP | GitHub | First-party | High | Low | Yes |
| Azure DevOps MCP | Microsoft | First-party | High | Low | No |
| Microsoft Docs MCP | Microsoft | First-party | High | Low | No |
| Context7 MCP | Upstash | Third-party | Medium | Medium | No |
| Mural skill MCP | hve-core | First-party | High | Medium | No |
| Attribute | Assessment |
|---|
| Operator | GitHub (Microsoft subsidiary) |
| Deployment | Remote (github.com hosted) or local |
| Authentication | OAuth, GitHub App tokens, PATs |
| Authorization | Inherits GitHub permission model |
| Data Handling | Data stays within GitHub ecosystem |
| Audit | GitHub audit log captures operations |
| Recommendation | Low risk; enable organization policies for access control |
| Attribute | Assessment |
|---|
| Operator | Microsoft |
| Deployment | Local only (npx invocation) |
| Authentication | Browser-based Azure AD login |
| Authorization | Inherits Azure DevOps permissions |
| Data Handling | No persistent storage by MCP server |
| Audit | Azure DevOps audit log |
| Recommendation | Low risk; standard Microsoft security practices apply |
| Attribute | Assessment |
|---|
| Operator | Microsoft |
| Deployment | Remote (learn.microsoft.com API) |
| Authentication | None required (public documentation) |
| Authorization | Rate limiting only |
| Data Handling | Read-only queries; no user data transmitted beyond search terms |
| Audit | Standard Microsoft API logging |
| Recommendation | Low risk; queries limited to public documentation |
| Attribute | Assessment |
|---|
| Operator | Upstash (third-party) |
| Deployment | Local client, Upstash backend |
| Authentication | API keys via Upstash dashboard |
| Authorization | Rate limiting, enterprise SSO available |
| Data Handling | Queries processed locally; only topics sent to backend |
| Audit | API logs with 30-day retention |
| Recommendation | Medium risk; evaluate topic extraction for sensitive context |
| Attribute | Assessment |
|---|
| Operator | hve-core (.github/skills/experimental/mural/) |
| Deployment | Local stdio (python -m mural mcp) |
| Authentication | Per-user Mural OAuth app via Authorization Code + PKCE loopback flow |
| Authorization | Inherits the granted Mural scope set; destructive tools re-check granted_scopes at dispatch |
| Data Handling | Tokens persisted to a per-user on-disk cache (mode 0600); Mural payloads returned as untrusted text in MCP tool results |
| Audit | stderr request log plus Mural API audit trail |
| Threat Model | Mural Skill Security Model; OAuth-flow STRIDE entries OA-1..OA-17 in OAuth Authentication Threats |
| Recommendation | Medium data-flow risk; treat all returned widget text as untrusted, restrict OAuth scopes via MURAL_SCOPES where possible |
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.
- First-party servers (GitHub, Azure DevOps, Microsoft Docs): Enable with organization policy controls; GitHub MCP is enabled by default
- Third-party servers (Context7): Evaluate data flow, use API key rotation, review Upstash trust center
Most skills are markdown knowledge packs with no runtime and are covered by the repository-level supply-chain and developer-workflow controls above.
Skills that ship an executable runtime (network egress, credential handling, subprocess execution, or untrusted document/content parsing) carry their own per-skill STRIDE threat model in a SECURITY.md next to their SKILL.md.
Those models follow a shared structure (assets, adversaries, trust buckets with per-bucket STRIDE mitigations, and an Enterprise Readiness Gaps register) and are the authoritative source for each skill's residual risk.
| Skill | Runtime surface | Primary residual gaps | Security model |
|---|
| jira | REST CLI; environment credentials | No token revocation; best-effort redaction; no cert pinning | SECURITY.md |
| gitlab | REST CLI; environment credentials; git-remote subprocess | Untrusted CI-trace egress; insecure-transport opt-out; no cert pinning | SECURITY.md |
| mural (experimental) | REST CLI; embedded stdio MCP server; OAuth token store | OAuth audit gaps; keyring backend toggle is code-execution surface | SECURITY.md |
| tts-voiceover (experimental) | Azure Speech egress; key/Entra credentials; SSML + PPTX parsing | Content egress to Azure region; broad credential chain | SECURITY.md |
| accessibility | Arbitrary-URL scan egress; unpinned npx @axe-core/cli subprocess | Unpinned scanner package; no egress allow-list (SSRF); headless-browser surface | SECURITY.md |
| powerpoint (experimental) | Sandboxed content-extra.py execution; LibreOffice/MuPDF document parsing | Denylist confinement is not OS-level; external-parser CVE exposure | SECURITY.md |
| video-to-gif (experimental) | Local CLI (bash + PowerShell); FFmpeg/ffprobe subprocess; untrusted media parsing | Inherited FFmpeg decoder CVE exposure; bare-filename search resolution | SECURITY.md |
| gh-code-scanning | GitHub code-scanning read via gh CLI subprocess; stdout only | Unpinned gh/jq PATH dependencies; TLS delegated to gh | SECURITY.md |
| customer-card-render (experimental) | Local Python CLI; regex parse of untrusted DT markdown; YAML emission | Inherited powerpoint build toolchain; confidential DT prose egress | SECURITY.md |
| vex | Local Python gate (vex_gate.py); anchored-regex parse of untrusted detection-issue body; json.loads of local OpenVEX doc; exit code only | Gate-suppression by issue-edit access; forced-proceed AI-credit consumption | SECURITY.md |
Skills whose scripts perform only local validation with no external surface (for example adr-author and vally-tests) do not require a dedicated model; their risk is bounded by the repository-level controls. When a new skill adds an executable runtime with any of the surfaces above, add a SECURITY.md following the shared structure and register it in this table and in the security documentation index.
| Metric | Threshold | Source |
|---|
| Dependency Pinning Compliance | ≥95% | dependency-pinning-scan.yml |
| SHA Staleness | ≤30 days | sha-staleness-check.yml |
| Dependency Review Fail | moderate | dependency-review.yml |
| npm Audit Fail Level | moderate | pr-validation.yml |
| Required PR Reviewers | 1 | Branch protection |
| Commitment | SLA | Source |
|---|
| Security Report Response | 24 hours | SECURITY.md |
| Governance Change Comment | 1 week | GOVERNANCE.md |
| Workflow | Trigger | Security Checks |
|---|
| pr-validation.yml | PR to main/develop | Pinning, npm audit, CodeQL, gitleaks |
| release-stable.yml | Push to main | Pinning, gitleaks, SBOM attestation, dependency diff (release) |
| codeql-analysis.yml | Push, PR, weekly | Static analysis |
| dependency-review.yml | PR to main/develop | Vulnerability scanning |
| weekly-security-maintenance.yml | Sundays 2 AM UTC | Pinning, staleness, CodeQL |
🤖 Crafted with precision by ✨Copilot following brilliant human instruction, then carefully refined by our team of discerning human reviewers.