Skip to content

Commit 1c728e7

Browse files
chore(security): dependency-review, invisible-char detection, and least-privilege workflow permissions (#782) (#783)
* deps: updating security posture in ci * deps: addressing feedback --------- Co-authored-by: Naved Merchant <naved.merchant@gmail.com>
1 parent 8d4ed32 commit 1c728e7

8 files changed

Lines changed: 84 additions & 2 deletions

File tree

.github/workflows/cli-release.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ on:
1313
type: boolean
1414
default: false
1515

16+
# Least privilege: the release job escalates to contents: write via its own
17+
# job-level permissions block.
18+
permissions:
19+
contents: read
20+
1621
jobs:
1722
# Build CLI for each platform.
1823
build:

.github/workflows/code-qa.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,56 @@ on:
99
merge_group:
1010
types: [checks_requested]
1111

12+
# Least privilege: every job below escalates only where it needs to.
13+
permissions:
14+
contents: read
15+
1216
jobs:
17+
dependency-review:
18+
runs-on: ubuntu-latest
19+
# Only meaningful for PRs — validates the dependency diff of the pull
20+
# request against GitHub's advisory database before merge.
21+
if: github.event_name == 'pull_request'
22+
steps:
23+
- name: Checkout code
24+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
25+
with:
26+
# This job never pushes — don't persist the GITHUB_TOKEN.
27+
persist-credentials: false
28+
- name: Dependency review
29+
uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
30+
31+
invisible-chars:
32+
runs-on: ubuntu-latest
33+
# Reject invisible / homoglyph Unicode that GitHub's diff UI renders
34+
# invisibly and most editors hide. These compile fine, which is the
35+
# risk: identifier-splitting, string-literal injection, and the
36+
# "Trojan Source" bidi-override attack (U+202A-U+202E). Scanning raw
37+
# bytes catches them in strings, identifiers, and comments alike.
38+
steps:
39+
- name: Checkout code
40+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
41+
with:
42+
# This job never pushes — don't persist the GITHUB_TOKEN.
43+
persist-credentials: false
44+
- name: Reject invisible / homoglyph Unicode
45+
run: |
46+
# zero-width (U+200B-200F), word joiner (U+2060), BOM (U+FEFF),
47+
# bidi overrides (U+202A-202E), soft hyphen (U+00AD).
48+
# Covers source, release-adjacent executable scripts
49+
# (*.sh / *.cjs / *.cts / *.mts), and the executable shell
50+
# blocks inside GitHub workflow/action YAML.
51+
if grep -rnP '[\x{200B}-\x{200F}\x{202A}-\x{202E}\x{2060}\x{FEFF}\x{00AD}]' \
52+
--include='*.ts' --include='*.tsx' --include='*.js' --include='*.mjs' \
53+
--include='*.cjs' --include='*.cts' --include='*.mts' --include='*.sh' \
54+
--include='*.yml' --include='*.yaml' \
55+
--exclude-dir=node_modules --exclude-dir=dist --exclude-dir=out \
56+
--exclude-dir=coverage --exclude-dir=.turbo --exclude-dir=.vinxi \
57+
src webview-ui packages apps .github; then
58+
echo "::error::Found invisible or homoglyph Unicode characters (zero-width / bidi-override / BOM / soft hyphen)"
59+
exit 1
60+
fi
61+
1362
check-translations:
1463
runs-on: ubuntu-latest
1564
steps:

.github/workflows/codeql.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ on:
77
schedule:
88
- cron: '24 19 * * 3'
99

10+
# Least privilege: the analyze job escalates to security-events: write via its
11+
# own job-level permissions block.
12+
permissions:
13+
contents: read
14+
1015
jobs:
1116
analyze:
1217
name: Analyze (${{ matrix.language }})

.github/workflows/e2e.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ on:
77
merge_group:
88
types: [checks_requested]
99

10+
permissions:
11+
contents: read
12+
1013
jobs:
1114
e2e-mock:
1215
runs-on: ubuntu-latest

.github/workflows/marketplace-publish.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ on:
66
- "v*.*.*"
77
workflow_dispatch:
88

9+
# Least privilege: publish-stable escalates to contents: write via its own
10+
# job-level permissions block.
11+
permissions:
12+
contents: read
13+
914
jobs:
1015
check-pr-approval:
1116
runs-on: ubuntu-latest

.github/workflows/release-validation.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ on:
1616
- "releases/**"
1717
- ".github/workflows/release-validation.yml"
1818

19+
permissions:
20+
contents: read
21+
1922
jobs:
2023
validate-release:
2124
runs-on: ubuntu-latest

.vscode/settings.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
"dist": true // set this to false to include "dist" folder in search results
1010
},
1111
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
12-
"typescript.tsc.autoDetect": "off",
13-
"vitest.disableWorkspaceWarning": true
12+
"js/ts.tsc.autoDetect": "off",
13+
// Surface invisible (zero-width) and ambiguous/homoglyph Unicode in the
14+
// editor. Pairs with the code-qa.yml invisible-chars CI check.
15+
"editor.unicodeHighlight.invisibleCharacters": true,
16+
"editor.unicodeHighlight.ambiguousCharacters": true
1417
}

packages/config-eslint/base.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,15 @@ export const config = [
3939
caughtErrorsIgnorePattern: "^_",
4040
},
4141
],
42+
// Reject irregular whitespace (incl. zero-width space U+200B and
43+
// BOM U+FEFF) in identifiers and between tokens. This rule does NOT
44+
// catch bidi-override, ZWJ/ZWNJ, or word-joiner characters; the CI
45+
// invisible-chars job in code-qa.yml is the authoritative defense
46+
// for the full Trojan Source character set across all files.
47+
"no-irregular-whitespace": [
48+
"error",
49+
{ skipStrings: true, skipComments: false, skipRegExps: true, skipTemplates: false },
50+
],
4251
},
4352
},
4453
]

0 commit comments

Comments
 (0)