From 8149a4546eff3e4bab3dbc395b867e93ac87d6b6 Mon Sep 17 00:00:00 2001 From: Shreya Keshive Date: Fri, 3 Oct 2025 13:25:28 -0400 Subject: [PATCH] feat(lint): add sensitive keyword linter (#10488) --- .github/workflows/ci.yml | 5 ++- scripts/lint.js | 88 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 476b20b494..864793d03a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -55,7 +55,7 @@ jobs: uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5 with: ref: '${{ github.event.inputs.branch_ref || github.ref }}' - fetch-depth: 1 + fetch-depth: 0 - name: 'Set up Node.js' uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4.4.0 @@ -87,6 +87,9 @@ jobs: - name: 'Run Prettier' run: 'node scripts/lint.js --prettier' + - name: 'Run sensitive keyword linter' + run: 'node scripts/lint.js --sensitive-keywords' + test_linux: name: 'Test (Linux)' runs-on: 'gemini-cli-ubuntu-16-core' diff --git a/scripts/lint.js b/scripts/lint.js index e28bc0a8b4..208216ca72 100644 --- a/scripts/lint.js +++ b/scripts/lint.js @@ -7,7 +7,13 @@ */ import { execSync } from 'node:child_process'; -import { mkdirSync, rmSync } from 'node:fs'; +import { + mkdirSync, + rmSync, + readFileSync, + existsSync, + lstatSync, +} from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; @@ -169,6 +175,82 @@ export function runPrettier() { } } +export function runSensitiveKeywordLinter() { + console.log('\nRunning sensitive keyword linter...'); + const SENSITIVE_PATTERN = /gemini-\d+(\.\d+)?/g; + const ALLOWED_KEYWORDS = new Set([ + 'gemini-2.5', + 'gemini-2.0', + 'gemini-1.5', + 'gemini-1.0', + ]); + + function getChangedFiles() { + const baseRef = process.env.GITHUB_BASE_REF || 'main'; + try { + execSync(`git fetch origin ${baseRef}`); + const mergeBase = execSync(`git merge-base HEAD origin/${baseRef}`) + .toString() + .trim(); + return execSync(`git diff --name-only ${mergeBase}..HEAD`) + .toString() + .trim() + .split('\n') + .filter(Boolean); + } catch (_error) { + console.error(`Could not get changed files against origin/${baseRef}.`); + try { + console.log('Falling back to diff against HEAD~1'); + return execSync(`git diff --name-only HEAD~1..HEAD`) + .toString() + .trim() + .split('\n') + .filter(Boolean); + } catch (_fallbackError) { + console.error('Could not get changed files against HEAD~1 either.'); + process.exit(1); + } + } + } + + const changedFiles = getChangedFiles(); + let violationsFound = false; + + for (const file of changedFiles) { + if (!existsSync(file) || lstatSync(file).isDirectory()) { + continue; + } + const content = readFileSync(file, 'utf-8'); + const lines = content.split('\n'); + let match; + while ((match = SENSITIVE_PATTERN.exec(content)) !== null) { + const keyword = match[0]; + if (!ALLOWED_KEYWORDS.has(keyword)) { + violationsFound = true; + const matchIndex = match.index; + let lineNum = 0; + let charCount = 0; + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (charCount + line.length + 1 > matchIndex) { + lineNum = i + 1; + const colNum = matchIndex - charCount + 1; + console.log( + `::warning file=${file},line=${lineNum},col=${colNum}::Found sensitive keyword "${keyword}". Please make sure this change is appropriate to submit.`, + ); + break; + } + charCount += line.length + 1; // +1 for the newline + } + } + } + } + + if (!violationsFound) { + console.log('No sensitive keyword violations found.'); + } +} + function main() { const args = process.argv.slice(2); @@ -190,6 +272,9 @@ function main() { if (args.includes('--prettier')) { runPrettier(); } + if (args.includes('--sensitive-keywords')) { + runSensitiveKeywordLinter(); + } if (args.length === 0) { setupLinters(); @@ -198,6 +283,7 @@ function main() { runShellcheck(); runYamllint(); runPrettier(); + runSensitiveKeywordLinter(); console.log('\nAll linting checks passed!'); } }