Security architecture and threat model
This document is the authoritative reviewer-facing security model for Copy GitHub Repository. It consolidates protected assets, trust assumptions, threat classes, controls, evidence, and residual risk without replacing the behavioral contract in product-contract.md, the architecture in architecture.md, the quality evidence model in quality-strategy.md, or the installation trust authority in installation-security.md.
SECURITY.md remains the vulnerability-reporting and supported-security-version front door.
Security objectives
The project is designed to:
- preserve the operator-approved source state rather than silently substituting newer state;
- prevent accidental overwrite/deletion of existing repositories;
- preserve repository identity and recovery evidence across replacement failures;
- avoid shell-string command execution and unnecessary credential exposure;
- fail closed when required host, identity, state, permission, or verification assumptions are not satisfied;
- make security-relevant evidence and residual risk auditable without overstating unimplemented controls.
Protected assets
| Asset | Security concern |
|---|---|
| Source repository identity and approved content state | substitution, TOCTOU drift, unauthorized mutation |
| Destination/archive repository identity | overwrite, identity confusion, loss of recovery path |
| GitHub CLI authentication material | disclosure, unintended reuse, logging |
| Repository settings/protection state | accidental weakening or incorrect restoration |
| Local temporary workspaces | tampering, stale/malformed content, resource failure |
| Recovery/provenance reports | integrity, accidental sensitive-data disclosure |
| Release ZIP/checksum, SBOM, provenance attestations, and future signing evidence | artifact substitution, publication compromise |
| Development/CI dependencies and workflow definitions | supply-chain compromise |
Actors and trust assumptions
- Operator / automation supplies intent, repository names, confirmation, local report destinations, and access through an already authenticated GitHub CLI context.
- CopyGitHubRepo module is trusted to enforce the product contract, safety checks, execution ordering, verification, and evidence semantics.
- PowerShell runtime is trusted to load and execute the module and helper scripts.
- Git, Git LFS, and GitHub CLI are external native prerequisites whose executables and behavior are outside the module’s direct control.
- GitHub.com is the supported remote platform for v0.1.0. API and Git transport availability, authorization, repository identity, and platform-side mutation are external trust dependencies.
- GitHub Actions / release environment is trusted for CI and release automation subject to repository permissions, workflow integrity, and configured secrets/environments.
- PowerShell Gallery and GitHub Releases are distribution services, not runtime dependencies of an installed module.
The project does not assume repository names are immutable identities. GitHub repository IDs/node IDs are used where available for identity-sensitive replacement checks.
Trust boundaries and sensitive-data flow
flowchart LR
Operator[Operator / automation]
Module[CopyGitHubRepo]
GHCLI[GitHub CLI auth context]
Native[Git / Git LFS / gh]
GitHub[GitHub.com APIs and Git transport]
Local[Temporary workspace / reports]
Release[GitHub Actions / Releases / PSGallery]
Operator --> Module
GHCLI --> Native
Module --> Native
Native --> GitHub
Module --> GitHub
Module --> Local
Release --> Operator
Credential/data rules:
- The module uses existing GitHub CLI authentication and does not intentionally collect, display, copy, or persist token values.
- HTTPS Git operations use GitHub CLI credentials through command-scoped Git configuration; interactive credential prompting is disabled.
- GitHub secrets, webhook secrets, private deploy-key material, GitHub App credentials, environment secrets, and similar secret-bearing configuration are outside the repository-copy contract.
- Normal results/recovery evidence may contain repository names, immutable repository IDs, commit/tree SHAs, mode, completed stages, failure stage, and verification/provenance evidence.
- Tokens, secret values, private keys, and unrelated private repository content must not be written intentionally to console output, reports, diagnostics, or CI artifacts.
- Diagnostic output is still a security boundary: future logging changes must be reviewed for credential and sensitive-content leakage.
Privileged and destructive operations
Security-sensitive mutations include:
- archive/rename of an existing destination or same-name source;
- destination/replacement repository creation;
- Git content and LFS publication;
- repository settings restoration;
- repository protection restoration;
- release/package publication by maintainer workflows.
Planning, -PlanOnly, and -WhatIf remain non-mutating. Before the first GitHub mutation, the application revalidates the immutable approved source state. Exact replacement confirmation and ShouldProcess boundaries are part of the authorization model; same-name destructive confirmation is not bypassed by ordinary confirmation suppression.
After a post-mutation failure, the product preserves known repositories and recovery evidence. It does not automatically delete repositories or rename archives back.
Threat and failure model
| Threat / failure class | Primary security effect | Current mitigation / behavior |
|---|---|---|
| Shell/command injection | arbitrary local command execution | centralized native-process helpers, discrete argument lists, custom security analyzer rules, engineering prohibition on shell-string evaluation / Invoke-Expression |
| Stale-state substitution / TOCTOU | operator approves one source state but another is copied | immutable SourceState, pre-mutation revalidation, workspace revalidation, fail-closed SourceStateChangedSincePlanning |
| Destination overwrite / destructive confusion | loss of existing repository | archive-before-replace, exact confirmation, immutable identity checks, distinct replacement identity |
| Repository-name identity confusion | wrong repository accepted after rename/recreate | immutable GitHub repository identity continuity where available |
| Credential leakage | token compromise | existing GitHub CLI auth context, no intentional token persistence/display, command-scoped credential configuration, analyzer detection for direct emission of secret-bearing variables |
| Malformed or adversarial remote state | unsafe execution or incorrect verification | validation/normalization, explicit supported-host boundary, approved-plan evidence, destination read-back verification |
| Native prerequisite compromise | arbitrary behavior below module boundary | documented prerequisite/trust boundary; centralized invocation and static boundary enforcement reduce but do not eliminate risk |
| Network/API interruption or throttling | incomplete operation / partial mutation | failures surface; preservation/recovery semantics apply; timeout/retry hardening is tracked separately |
| Local disk/temp resource failure | interrupted copy/evidence loss | failure is not treated as remote success; recovery guidance applies; disk preflight improvements are tracked separately |
| Partial mutation | archive/destination may exist after failure | explicit completed-stage/recovery evidence; no automatic rollback/delete |
| Settings/protection restoration failure | verified content with incomplete configuration | content verifies first, settings then protection; failure is reported as a distinct late mutation state |
| Release artifact substitution | user installs unintended bytes | SHA-256 integrity comparison plus GitHub build-provenance and SBOM attestations bind release evidence to the versioned ZIP; independent publisher signing remains separate under #80 |
| Dependency/workflow compromise | CI/release or development environment compromise | exact dependency/action pinning, reviewed development dependency versions, scheduled dependency monitoring, and CodeQL analysis of GitHub Actions workflows; live repository security-setting verification remains #79 |
| Persistent PSGallery trust mutation during dev setup | workstation trust posture altered | development dependency installer targets PSGallery explicitly without changing repository InstallationPolicy; regression is protected by DevelopmentDependencies.Tests.ps1 |
Security control and evidence matrix
Status vocabulary:
- Implemented — enforced in repository code/configuration.
- Automatically verified — protected by tests/analyzers/workflows.
- Platform-provided / live verification required — depends on live GitHub/repository configuration and must not be inferred from source alone.
- Planned / open — tracked work that is not yet a current control.
- Residual risk — risk that remains after current controls.
| Control | Purpose | Current implementation / authority | Evidence | Status |
|---|---|---|---|---|
| Approved immutable source state | TOCTOU/substitution defense | planning + approved-plan execution + workspace checks | ApprovedSourceState.Tests.ps1, StaleStateSafety.Tests.ps1, architecture fitness functions |
Implemented + automatically verified |
| Archive-before-replace and identity continuity | destructive-operation safety | replacement orchestration/product contract | replacement/same-name integration tests and E2E harnesses | Implemented + automatically verified; live evidence remains release-specific |
| Distinct replacement identity | prevents renamed repository from masquerading as replacement | immutable repository ID checks where available | same-name/replacement tests | Implemented + automatically verified |
| No automatic rollback/delete | preservation and forensic/recovery safety | recovery/result contract | recovery/risk-failure tests | Implemented + automatically verified |
| Centralized native process invocation | command-injection/quoting boundary | process helpers using argument lists/no shell evaluation | native-command tests + PSScriptAnalyzer/style contracts | Implemented + automatically verified |
| Credential minimization | reduce token exposure | existing gh auth, command-scoped helper, no intentional token persistence/output |
security/documentation contracts and implementation review | Implemented; not a guarantee against compromised prerequisites |
| GitHub.com-only host guard | prevents unvalidated host semantics | explicit fail-closed host checks | HostName.Tests.ps1 |
Implemented + automatically verified |
| Content-before-settings/protection ordering | prevents protection from blocking initial publication and makes late failure explicit | application orchestration | settings/protection tests | Implemented + automatically verified |
| Release ZIP SHA-256 check | integrity/corruption detection | stable installer/release contract | installation-security and release contract tests | Implemented contract; does not independently authenticate publisher |
| Development dependency version review | reduce stale development-tool risk | exact pins in build/DevelopmentDependencies.psd1; review record below |
cross-platform Quality Gate and upstream release/advisory review | Implemented + automatically verified for selected pins |
| Security-focused custom PowerShell SAST rules | detect high-signal PowerShell security regressions | Measure-CgrSecurity in the repository custom analyzer module |
PSScriptAnalyzerSecurityRules.Tests.ps1 + normal recursive PSScriptAnalyzer Quality Gate |
Implemented + automatically verified |
| CodeQL for GitHub Actions | workflow-code static analysis | .github/workflows/codeql.yml, language actions, security-extended queries |
CodeQLWorkflow.Tests.ps1 + successful live CodeQL workflow execution |
Implemented + automatically verified for GitHub Actions workflows; does not scan PowerShell source |
| Automated dependency/supply-chain monitoring | outdated/vulnerable dependency visibility | .github/dependabot.yml, .github/workflows/dependency-monitor.yml, dependency-monitoring.md |
DevelopmentDependencies.Tests.ps1 + scheduled/manual workflow contract |
Implemented + automatically verified |
| Live GitHub security-setting baseline | secret scanning, push protection, Dependabot/code scanning settings | issue #79 | live repository settings | Platform-provided / live verification required; currently open |
| Independent release authenticity/signing | publisher/artifact authenticity | issue #80 | future signature/provenance verification | Planned / open |
| PSGallery trust-policy preservation | avoid persistent development-host trust mutation | dependency installer does not call Set-PSRepository; exact versions are installed from explicit PSGallery source |
DevelopmentDependencies.Tests.ps1, cross-platform Quality Gate |
Implemented + automatically verified |
| SBOM / provenance evidence | component/release assurance | build/New-ReleaseSbom.ps1, .github/workflows/release.yml, release-sbom.md |
ReleaseSbom.Tests.ps1, release workflow contracts, cross-platform Quality Gate #686 |
Implemented + automatically verified |
Release SBOM and provenance evidence
Issue #98 is completed. Stable release automation now generates SPDX 2.3 JSON from the completed deterministic release ZIP rather than from an unrelated mutable source tree. build/New-ReleaseSbom.ps1 records the exact shipped file inventory, SHA-256 file and artifact checksums, package/version identity, source commit, deterministic creation metadata, and the SPDX 2.x package verification code required by that format.
The SBOM package graph represents shipped runtime content only. Runtime PowerShell module dependencies are currently none; PowerShell 7.4+, Git, GitHub CLI, and conditional Git LFS are classified as external prerequisites rather than shipped DEPENDS_ON packages. Pester, PSScriptAnalyzer, GitHub Actions dependencies, GitHub Releases, and PowerShell Gallery are development/CI/distribution concerns and are not represented as installed runtime dependencies.
The stable release workflow publishes the .spdx.json sidecar and uses the immutable-pinned consolidated actions/attest action for both build provenance and SBOM attestation of the same versioned ZIP. Required attestation failures fail the release workflow before publication. See release-sbom.md for retrieval, verification, classification, and reproducibility details.
These controls strengthen artifact provenance and machine-readable software inventory, but they do not replace independent publisher signing. GitHub attestations establish statements about artifact digest, workflow/repository identity, and predicates within GitHub’s attestation model; they do not independently authenticate a separately managed publisher key, prove vulnerability absence, or make semantic SBOM classification infallible. Independent signing remains the distinct #80 control.
Dependency and trust inventory
| Class | Current dependency / service | Security posture |
|---|---|---|
| Runtime platform | PowerShell 7.4+ | external prerequisite |
| Runtime native prerequisite | Git | external executable/trust boundary |
| Runtime native prerequisite | GitHub CLI | authentication/API prerequisite |
| Conditional runtime prerequisite | Git LFS | required for FullHistory and Snapshot with approved LFS content |
| Runtime PowerShell modules | none declared by the module manifest | no third-party PowerShell runtime dependency currently shipped |
| Development modules | Pester 6.1.0, PSScriptAnalyzer 1.25.0 | exact-version pinned; weekly freshness/advisory monitoring is implemented under #78 |
| CI actions | GitHub Actions referenced by workflows | immutable SHA pinning is repository policy; Dependabot proposes reviewed updates and CodeQL analyzes workflow code |
| Runtime remote service | GitHub.com | v0.1.0 supported host and primary external service |
| Distribution | GitHub Releases, PowerShell Gallery | release/distribution trust boundaries; not installed-module runtime dependencies |
Development dependency review
The project reviews stable dependency releases before adoption rather than automatically taking every available update:
- PSScriptAnalyzer 1.25.0 was selected over 1.24.0. It is compatible with the project’s PowerShell 7.4+ baseline and includes analyzer/runtime dependency updates plus additional rules and fixes. The complete repository Quality Gate proved compatibility with this project before #75 was closed.
- Pester 6.1.0 is the current exact test-framework pin. The project completed the Pester 6 migration and validates the repository test suite against 6.1.0. The matching reviewed-stable baseline is recorded in
build/DevelopmentDependencyMonitoring.psd1. - The upstream GitHub repository security-advisory APIs are checked for both Pester and PSScriptAnalyzer. This is evidence of the configured advisory review source, not a claim that either project can have no undiscovered vulnerabilities.
- Exact version pins remain mandatory. Ongoing dependency/supply-chain monitoring is implemented by #78 and documented in
dependency-monitoring.md.
Security static-analysis policy
The repository custom PSScriptAnalyzer module adds Measure-CgrSecurity to the normal PSScriptAnalyzerSettings.psd1 path. The rule currently targets high-confidence patterns in production and operational PowerShell:
Invoke-Expression/iexdynamic evaluation;- explicit
cmd /c,sh -c,bash -c, orzsh -cshell interpretation; - direct
git,gh, orgit-lfsinvocation from module source that bypasses the centralized native-process boundary; - direct output/diagnostic emission of variables whose names indicate token, password, secret, credential, private-key, API-key, or authentication material.
The policy intentionally avoids speculative heuristics that would create routine false positives. A clean analyzer result therefore means the defined patterns were not found; it does not mean arbitrary PowerShell vulnerabilities cannot exist.
Run the policy locally through the canonical project gate:
./build/Test-Project.ps1
For focused contract verification of the security analyzer’s positive detections and safe cases, run:
./build/Test-Project.ps1 -Category Contract
GitHub Actions CodeQL policy
The repository uses .github/workflows/codeql.yml to analyze GitHub Actions workflow code with CodeQL’s actions language and security-extended query suite. The workflow runs when workflow definitions change on main or in pull requests targeting main, on a weekly schedule, and by manual dispatch. Third-party actions are pinned to immutable commit SHAs, checkout does not persist credentials, and the analysis job declares only the permissions required to read repository/action metadata and upload code-scanning results.
The initial CodeQL workflow execution completed successfully. Repository contract tests verify the trigger scope, immutable pins, least-privilege permissions, language selection, query suite, and absence of pull_request_target.
This control is intentionally scoped to GitHub Actions workflow code. It does not analyze the project’s PowerShell source. PowerShell security analysis remains the responsibility of the repository-owned PSScriptAnalyzer policy, Pester tests, review, and other assurance controls. A successful CodeQL run means the configured GitHub Actions analysis completed successfully; it is not proof that all workflow or product vulnerabilities are absent.
Security verification strategy and limits
Current verification combines:
- Pester Unit/Integration/Contract suites for safety semantics and failure behavior;
- repository-owned PSScriptAnalyzer policy, including the custom
Measure-CgrSecurityrule, plus general PowerShell static analysis; - CodeQL analysis of GitHub Actions workflow code;
- workflow/package/documentation contract testing;
- controlled E2E harnesses for selected live GitHub behaviors;
- manual/live verification where platform configuration cannot be proved from repository files.
These mechanisms are not comprehensive SAST. PowerShell source is not covered by a CodeQL language analysis in this project; GitHub Actions workflow code is covered separately by CodeQL. A passing Quality Gate and CodeQL run are strong conformance evidence for the checks that exist; they are not proof that the software is vulnerability-free.
E2E-capable also does not mean a specific release candidate was live-validated. Exact-release-candidate evidence is governed by quality-strategy.md and the release-readiness process.
Privacy and telemetry posture
The project does not define a telemetry/analytics subsystem. It operates on operator-selected GitHub repositories and local paths using the operator’s authenticated GitHub context. It does not intentionally transmit usage analytics to the project maintainer.
Repository content necessarily crosses Git/GitHub transport boundaries as part of the requested copy operation. Local reports/recovery artifacts remain operator-controlled files. Future telemetry, crash reporting, or centralized diagnostics would be a material product/security/privacy change and would require explicit design and documentation rather than being added implicitly.
Residual risk and open hardening
For v0.1.0 review, the following must remain explicit until resolved or accepted by release readiness:
- #75 — completed: PSScriptAnalyzer 1.25.0 and Pester 6.1.0 are the current exact development pins after dependency review and validation.
- #76 — completed: focused AST-based security analyzer rules are loaded by the normal PSScriptAnalyzer path and protected by positive/safe contract tests.
- #77 — completed: CodeQL analyzes GitHub Actions workflow code with immutable action pins, least-privilege permissions, contract coverage, and a successful initial live scan; PowerShell remains outside CodeQL language coverage.
- #78 — completed: Dependabot monitors GitHub Actions updates and a separate scheduled/manual workflow monitors PowerShell development dependency freshness and upstream published advisories.
- #79 — live repository security settings have not yet been closed as verified.
- #80 — independent release authenticity/signing is open; same-channel SHA-256 and GitHub attestations remain distinct from independent publisher authentication.
- #81 — completed: development dependency installation no longer changes PSGallery trust policy; regression coverage prevents reintroduction of
Set-PSRepository/InstallationPolicymutation. - #98 — completed: stable release packaging generates deterministic SPDX 2.3 JSON from the exact release ZIP, publishes it as release evidence, and requires GitHub build-provenance and SBOM attestations; this does not replace independent publisher signing.
- Native prerequisite compromise (PowerShell/Git/Git LFS/GitHub CLI) remains outside the module’s direct enforcement boundary.
- GitHub.com availability, authorization, API behavior, and remote-side consistency are external dependencies.
- Resource exhaustion, timeout, retry, throttling, and interruption hardening tracked under the non-functional follow-up work remains relevant to availability and partial-mutation risk.
Open security work is not automatically a release blocker merely because it is open; #104 must classify blocker versus accepted limitation using evidence and explicit decision authority.
Reporting and incident response
Suspected vulnerabilities should follow SECURITY.md, not public issue disclosure. Serious post-publication security, credential, supply-chain, artifact, or distribution incidents will use the process introduced by #108 once implemented.
Until then, preservation principles still apply: retain exact affected version/commit/artifact identities and relevant non-secret evidence; do not silently overwrite immutable release evidence or destroy repository recovery state.