Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 41 additions & 4 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ name: Docker

on:
push:
tags: ['v*']
tags: ["v*"]
workflow_dispatch:

# QNBS-v3: top-level read-only follows principle of least privilege (CodeQL Token-Permissions).
Expand Down Expand Up @@ -69,12 +69,49 @@ jobs:
id: result
run: echo "audited-sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"

verify-release-tag:
name: Verify release tag
if: >-
${{ github.event_name == 'push' && github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/v') && github.ref_protected == true }}
runs-on: ubuntu-latest
permissions:
contents: read
outputs:
verified-tag-sha: ${{ steps.verify.outputs.verified-tag-sha }}
steps:
- name: Check out the pinned trusted verifier
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: 147470682b62d5bfe196bba803353d9f25cab8a7
path: trusted-verifier
fetch-depth: 1
persist-credentials: false
- name: Verify GitHub signatures for the release tag
id: verify
env:
GITHUB_EVENT_NAME: ${{ github.event_name }}
GITHUB_EVENT_PATH: ${{ github.event_path }}
GITHUB_REF_NAME: ${{ github.ref_name }}
Comment thread
qnbs marked this conversation as resolved.
Resolved
GITHUB_REF_TYPE: ${{ github.ref_type }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_SHA: ${{ github.sha }}
GITHUB_TOKEN: ${{ github.token }}
shell: bash
working-directory: trusted-verifier
run: >-
set -euo pipefail && env -i PATH="$PATH" GITHUB_EVENT_NAME="$GITHUB_EVENT_NAME"
GITHUB_EVENT_PATH="$GITHUB_EVENT_PATH" GITHUB_REF_NAME="$GITHUB_REF_NAME"
GITHUB_REF_TYPE="$GITHUB_REF_TYPE" GITHUB_REPOSITORY="$GITHUB_REPOSITORY"
GITHUB_SHA="$GITHUB_SHA" GITHUB_TOKEN="$GITHUB_TOKEN"
node scripts/signing/verify-github-signatures.mjs &&
echo "verified-tag-sha=$GITHUB_SHA" >> "$GITHUB_OUTPUT"

build-push:
name: Build & push image
# QNBS-v3 (#911): every pushed tag (version, major.minor, and `latest` from metadata-action's latest=auto or the raw rule) waits on a successful audit of this exact SHA; no status function, so a failed, skipped or cancelled audit leaves this job unrun.
needs: [security-audit]
# QNBS-v3 (#911): publish only a verified signed v* tag push after the exact-SHA audit.
needs: [security-audit, verify-release-tag]
if: >-
${{ needs.security-audit.result == 'success' && needs.security-audit.outputs.audited-sha == github.sha }}
${{ github.event_name == 'push' && github.ref_type == 'tag' && startsWith(github.ref, 'refs/tags/v') && github.ref_protected == true && needs.verify-release-tag.result == 'success' && needs.verify-release-tag.outputs.verified-tag-sha == github.sha && needs.security-audit.result == 'success' && needs.security-audit.outputs.audited-sha == github.sha }}
runs-on: ubuntu-latest
timeout-minutes: 20
# QNBS-v3: packages:write scoped to this job only — prevents privilege escalation in future jobs.
Expand Down
35 changes: 29 additions & 6 deletions .github/workflows/release-gate-proof.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Non-publishing proof of the #911 release security gate (same inlined audit job as tauri-build.yml / docker.yml). Dispatch it on main with:
# expected-sha = the dispatched commit -> audit succeeds, the publish stand-in runs (echo only)
# expected-sha = another full SHA -> audit may succeed, the stand-in is skipped (SHA mismatch)
# expected-sha = not-a-sha -> audit fails, the stand-in is skipped
# The stand-in's `if` is identical to docker.yml build-push (tests/unit/tooling/releaseSecurityGate.test.ts).
# Non-publishing proof of the #911 release security gate. Dispatch on main with:
# expected-sha = dispatch SHA; simulated_event=push; simulated_ref_type=tag;
# simulated_ref=refs/tags/v1.29.3; simulated_tag_signature=verified -> stand-in runs
# A wrong SHA, non-tag ref, unverified signature result, or non-push simulation keeps it skipped.
# Event/ref/signature inputs are data only; this workflow has no publishing permission or action.
# Nothing here holds a write permission, logs in to a registry or creates a release.
name: Release gate proof

Expand All @@ -13,6 +13,29 @@ on:
description: SHA to hand to the release security audit (use a wrong or invalid value for the negative proofs).
required: true
type: string
simulated_event:
description: Event modeled by the no-write stand-in; choose push only for the positive tag-path proof.
required: true
type: choice
options: [push, workflow_dispatch]
default: workflow_dispatch
simulated_ref_type:
description: Ref type modeled by the no-write stand-in.
required: true
type: choice
options: [branch, tag]
default: branch
simulated_ref:
description: Ref modeled by the no-write stand-in, for example refs/tags/v1.29.3.
required: true
type: string
default: refs/heads/main
simulated_tag_signature:
description: Simulated signature-verifier result; this input does not verify a real tag.
required: true
type: choice
options: [verified, rejected]
default: rejected

permissions:
contents: read
Expand Down Expand Up @@ -77,7 +100,7 @@ jobs:
name: Publish stand-in (no side effects)
needs: [security-audit]
if: >-
${{ needs.security-audit.result == 'success' && needs.security-audit.outputs.audited-sha == github.sha }}
${{ github.event_name == 'workflow_dispatch' && inputs.simulated_event == 'push' && inputs.simulated_ref_type == 'tag' && startsWith(inputs.simulated_ref, 'refs/tags/v') && inputs.simulated_tag_signature == 'verified' && needs.security-audit.result == 'success' && needs.security-audit.outputs.audited-sha == github.sha }}
runs-on: ubuntu-latest
timeout-minutes: 2
permissions:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

- **Container publishing:** GHCR releases now require a protected, signed annotated `v*` tag bound to the triggering commit, verified by a pinned source, and the exact-commit security audit; manual dispatch remains non-publishing (PR #1205).
- **Release-tag verification:** Validate protected-main ancestry with the GitHub Compare API's returned base and merge-base commits, and bind the verified tag target to the triggering event SHA (PR #1207).

<!-- release-candidate: v1.29.3 -->
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<img src="https://img.shields.io/badge/TypeScript-tsgo_native_preview-3178C6?logo=typescript&logoColor=white" alt="TypeScript native preview (tsgo)">
<img src="https://img.shields.io/badge/Desktop-Tauri_2-FFC131?logo=tauri&logoColor=black" alt="Tauri 2">
<img src="https://img.shields.io/badge/i18n-19_locales_%2F_2963_keys-0EA5E9" alt="19 locales — 2963 keys">
<img src="https://img.shields.io/badge/Tests-8705%2B_%2F_656_files-22C55E" alt="8705+ tests / 656 files">
<img src="https://img.shields.io/badge/Tests-8721%2B_%2F_656_files-22C55E" alt="8721+ tests / 656 files">
<img src="https://img.shields.io/codecov/c/github/qnbs/WorldScript-Studio?logo=codecov&label=Coverage" alt="Codecov Coverage">
<img src="https://img.shields.io/github/actions/workflow/status/qnbs/WorldScript-Studio/.github/workflows/ci.yml?branch=main&logo=github&label=CI" alt="CI status">
<img src="https://img.shields.io/badge/License-MIT-22C55E" alt="MIT License">
Expand Down Expand Up @@ -807,7 +807,7 @@ The normal web PR pipeline is not the complete native release qualification surf
| DOCX | `docx` + JSZip | Word-compatible export |
| PWA | Service Worker + Web App Manifest | Offline shell/installability |
| i18n | Custom React i18n context | 2963 keys × 19 locales |
| Testing | Vitest 5.x (8705+ tests / 656 files) + Playwright | Unit/integration/E2E |
| Testing | Vitest 5.x (8721+ tests / 656 files) + Playwright | Unit/integration/E2E |
| Quality | Biome + tsgo + CodeQL/security tooling | Static and CI gates |
| Desktop | Tauri 2 | Current native shell |

Expand Down Expand Up @@ -884,7 +884,7 @@ WorldScript-Studio/
├── locales/ # Source locale trees
├── public/ # PWA assets, manifest, SW, runtime locale bundles
├── tests/
│ ├── unit/ # Vitest unit tests (the 8705+ total also includes components/ and packages/*/tests; tests/e2e is excluded)
│ ├── unit/ # Vitest unit tests (the 8721+ total also includes components/ and packages/*/tests; tests/e2e is excluded)
│ └── e2e/ # Playwright
├── docs/ # Canonical product/engineering documentation + ADRs
├── src-tauri/ # Tauri v2 desktop shell / Rust
Expand Down Expand Up @@ -1029,7 +1029,7 @@ Raw bundle-budget ceilings (KB per uncompressed asset): entry **2500 KB**, vendo

Current source-synchronized README metrics:

- **8705+ unit tests** across **656 test files**
- **8721+ unit tests** across **656 test files**
- i18n: **2963 keys × 19 locales**

CI remains authoritative for actual pass/fail and live coverage.
Expand Down
Loading
Loading