diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 000000000..c912ff510 --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,47 @@ +title = "OpenPencil Gitleaks configuration" + +[extend] +useDefault = true + +[[allowlists]] +description = "Local and generated artifacts" +paths = [ + '''(^|/)\.env$''', + '''(^|/)\.worktrees/''', + '''(^|/)dist/''', + '''(^|/)node_modules/''', + '''^desktop/target/''', + '''^packages/[^/]+/dist/''', + '''^test-results/''', + '''^scratch/''' +] + +[[allowlists]] +description = "Public Google Fonts browser API key" +regexTarget = "line" +paths = ['''packages/core/src/constants\.ts'''] +regexes = ['''GOOGLE_FONTS_API_KEY'''] + +[[allowlists]] +description = "Keyboard shortcut metadata is not a secret" +regexTarget = "line" +paths = ['''packages/vue/src/editor/commands/registry\.ts'''] +regexes = ['''keybinding:\s*['\"][^'\"]+['\"]'''] + +[[allowlists]] +description = "Documented dummy OpenRouter test key" +regexTarget = "line" +paths = ['''tests/e2e/chat/panel\.spec\.ts'''] +regexes = ['''sk-or-test-key-12345'''] + +[[allowlists]] +description = "Figma fixture identifiers are public test metadata" +regexTarget = "line" +paths = [ + '''tests/engine/io/fig/heavy/component-metadata\.test\.ts''', + '''tests/fixtures/figma-oracles/.*\.json''' +] +regexes = [ + '''componentKey''', + '''"fileKey"''' +] diff --git a/package.json b/package.json index b7497e0e0..f4489af46 100644 --- a/package.json +++ b/package.json @@ -26,9 +26,10 @@ "lint:structure": "oxlint -c oxlint.json vite.config.ts vite/ src/ packages/scene-graph/src/ packages/scene-graph/scripts/ packages/core/src/ packages/vue/src/ packages/cli/src/ packages/mcp/src/ packages/dom-css/src/ packages/dom-css/tests/ packages/dom-css/scripts/ packages/pen/src/ packages/pen/scripts/ packages/kiwi/src/ packages/kiwi/tests/ packages/kiwi/scripts/ packages/fig/src/ packages/fig/tests/ packages/fig/scripts/ tests/ scripts/ tools/", "format": "oxfmt --write .oxfmtrc.json vite.config.ts vite/ src/ packages/scene-graph/src/ packages/scene-graph/scripts/ packages/core/src/ packages/cli/src/ packages/mcp/src/ packages/vue/src/ packages/dom-css/src/ packages/dom-css/tests/ packages/dom-css/scripts/ packages/pen/src/ packages/pen/scripts/ packages/kiwi/src/ packages/kiwi/tests/ packages/kiwi/scripts/ packages/fig/src/ packages/fig/tests/ packages/fig/scripts/ tests scripts/ tools/", "format:check": "bun run format && status=$(git status --porcelain -uall) && test -z \"$status\" || (echo \"$status\" && exit 1)", - "check": "bun run build:packages && bun run lint && tsgo --noEmit && bun run check:vue && bun run check:i18n && bun run check:packages && bun run check:deps && bun run check:audit && bun run check:monorepo && bun run check:arch && bun run test:type-shapes && bun run test:tools && bun run test:dupes", + "check": "bun run build:packages && bun run lint && tsgo --noEmit && bun run check:vue && bun run check:i18n && bun run check:packages && bun run check:deps && bun run check:audit && bun run check:secrets && bun run check:monorepo && bun run check:arch && bun run test:type-shapes && bun run test:tools && bun run test:dupes", "check:deps": "knip --include unlisted,unresolved,binaries", "check:audit": "bun audit --audit-level=critical", + "check:secrets": "bun tools/secret-scan/src/index.ts", "check:monorepo": "sherif --ignore-rule root-package-dependencies", "check:i18n": "bun tools/i18n/src/check-locales.ts", "check:packages": "bun tools/package-quality/src/check/metadata.ts && bun tools/package-quality/src/check/publint.ts && bun tools/package-quality/src/check/attw.ts", diff --git a/tools/secret-scan/package.json b/tools/secret-scan/package.json new file mode 100644 index 000000000..ff7609dfb --- /dev/null +++ b/tools/secret-scan/package.json @@ -0,0 +1,6 @@ +{ + "name": "@open-pencil/tools-secret-scan", + "version": "0.0.0", + "private": true, + "type": "module" +} diff --git a/tools/secret-scan/src/index.ts b/tools/secret-scan/src/index.ts new file mode 100644 index 000000000..90da2f43d --- /dev/null +++ b/tools/secret-scan/src/index.ts @@ -0,0 +1,27 @@ +const GITLEAKS_VERSION = 'v8.30.1' +const GITLEAKS_MODULE = `github.com/zricethezav/gitleaks/v8@${GITLEAKS_VERSION}` + +const gitleaksArgs = ['dir', '--config', '.gitleaks.toml', '--redact', '--no-banner', '.'] + +function run(command: string, args: string[]): Bun.SpawnSyncReturns | null { + try { + return Bun.spawnSync([command, ...args], { + stdout: 'inherit', + stderr: 'inherit' + }) + } catch (error) { + if (error instanceof Error && 'code' in error && error.code === 'ENOENT') { + return null + } + throw error + } +} + +const proc = run('gitleaks', gitleaksArgs) ?? run('go', ['run', GITLEAKS_MODULE, ...gitleaksArgs]) + +if (!proc?.success) { + console.error('Secret scan failed.') + process.exit(proc?.exitCode || 1) +} + +console.log('Secret scan passed.')