diff --git a/.github/workflows/cli-release.yml b/.github/workflows/cli-release.yml index 78eb63dd7f..a5770440ed 100644 --- a/.github/workflows/cli-release.yml +++ b/.github/workflows/cli-release.yml @@ -13,6 +13,11 @@ on: type: boolean default: false +# Least privilege: the release job escalates to contents: write via its own +# job-level permissions block. +permissions: + contents: read + jobs: # Build CLI for each platform. build: diff --git a/.github/workflows/code-qa.yml b/.github/workflows/code-qa.yml index 76abccf103..6dc7b33d9d 100644 --- a/.github/workflows/code-qa.yml +++ b/.github/workflows/code-qa.yml @@ -9,7 +9,56 @@ on: merge_group: types: [checks_requested] +# Least privilege: every job below escalates only where it needs to. +permissions: + contents: read + jobs: + dependency-review: + runs-on: ubuntu-latest + # Only meaningful for PRs — validates the dependency diff of the pull + # request against GitHub's advisory database before merge. + if: github.event_name == 'pull_request' + steps: + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + # This job never pushes — don't persist the GITHUB_TOKEN. + persist-credentials: false + - name: Dependency review + uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0 + + invisible-chars: + runs-on: ubuntu-latest + # Reject invisible / homoglyph Unicode that GitHub's diff UI renders + # invisibly and most editors hide. These compile fine, which is the + # risk: identifier-splitting, string-literal injection, and the + # "Trojan Source" bidi-override attack (U+202A-U+202E). Scanning raw + # bytes catches them in strings, identifiers, and comments alike. + steps: + - name: Checkout code + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + # This job never pushes — don't persist the GITHUB_TOKEN. + persist-credentials: false + - name: Reject invisible / homoglyph Unicode + run: | + # zero-width (U+200B-200F), word joiner (U+2060), BOM (U+FEFF), + # bidi overrides (U+202A-202E), soft hyphen (U+00AD). + # Covers source, release-adjacent executable scripts + # (*.sh / *.cjs / *.cts / *.mts), and the executable shell + # blocks inside GitHub workflow/action YAML. + if grep -rnP '[\x{200B}-\x{200F}\x{202A}-\x{202E}\x{2060}\x{FEFF}\x{00AD}]' \ + --include='*.ts' --include='*.tsx' --include='*.js' --include='*.mjs' \ + --include='*.cjs' --include='*.cts' --include='*.mts' --include='*.sh' \ + --include='*.yml' --include='*.yaml' \ + --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=out \ + --exclude-dir=coverage --exclude-dir=.turbo --exclude-dir=.vinxi \ + src webview-ui packages apps .github; then + echo "::error::Found invisible or homoglyph Unicode characters (zero-width / bidi-override / BOM / soft hyphen)" + exit 1 + fi + check-translations: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index a7ca23bd9d..08f88e5693 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -7,6 +7,11 @@ on: schedule: - cron: '24 19 * * 3' +# Least privilege: the analyze job escalates to security-events: write via its +# own job-level permissions block. +permissions: + contents: read + jobs: analyze: name: Analyze (${{ matrix.language }}) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index ab25568b9a..eba2426d02 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -7,6 +7,9 @@ on: merge_group: types: [checks_requested] +permissions: + contents: read + jobs: e2e-mock: runs-on: ubuntu-latest diff --git a/.github/workflows/marketplace-publish.yml b/.github/workflows/marketplace-publish.yml index 9448d2e400..93fd732762 100644 --- a/.github/workflows/marketplace-publish.yml +++ b/.github/workflows/marketplace-publish.yml @@ -6,6 +6,11 @@ on: - "v*.*.*" workflow_dispatch: +# Least privilege: publish-stable escalates to contents: write via its own +# job-level permissions block. +permissions: + contents: read + jobs: check-pr-approval: runs-on: ubuntu-latest diff --git a/.github/workflows/release-validation.yml b/.github/workflows/release-validation.yml index 7d7ea410b3..0603b71277 100644 --- a/.github/workflows/release-validation.yml +++ b/.github/workflows/release-validation.yml @@ -16,6 +16,9 @@ on: - "releases/**" - ".github/workflows/release-validation.yml" +permissions: + contents: read + jobs: validate-release: runs-on: ubuntu-latest diff --git a/.vscode/settings.json b/.vscode/settings.json index 6eb636c982..9d429ef11f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,6 +9,9 @@ "dist": true // set this to false to include "dist" folder in search results }, // Turn off tsc task auto detection since we have the necessary tasks as npm scripts - "typescript.tsc.autoDetect": "off", - "vitest.disableWorkspaceWarning": true + "js/ts.tsc.autoDetect": "off", + // Surface invisible (zero-width) and ambiguous/homoglyph Unicode in the + // editor. Pairs with the code-qa.yml invisible-chars CI check. + "editor.unicodeHighlight.invisibleCharacters": true, + "editor.unicodeHighlight.ambiguousCharacters": true } diff --git a/packages/config-eslint/base.js b/packages/config-eslint/base.js index abb708016c..6a15f09c03 100644 --- a/packages/config-eslint/base.js +++ b/packages/config-eslint/base.js @@ -39,6 +39,15 @@ export const config = [ caughtErrorsIgnorePattern: "^_", }, ], + // Reject irregular whitespace (incl. zero-width space U+200B and + // BOM U+FEFF) in identifiers and between tokens. This rule does NOT + // catch bidi-override, ZWJ/ZWNJ, or word-joiner characters; the CI + // invisible-chars job in code-qa.yml is the authoritative defense + // for the full Trojan Source character set across all files. + "no-irregular-whitespace": [ + "error", + { skipStrings: true, skipComments: false, skipRegExps: true, skipTemplates: false }, + ], }, }, ]