Skip to content

Commit 3040b27

Browse files
test: run git-secrets during CI #3387
Problem: No CI check to prevent accidental commit of secrets. Solution: - Create a new test directory called testLint/ which will hold our code linting related tests. - Create a git-secrets test in testLint/ which runs git-secrets on our codebase. - Move eslint script to testLint/ - Add a new "Lint" CI job that runs the testLint/ tests. Signed-off-by: Nikolas Komonen <[email protected]>
1 parent afaae8a commit 3040b27

File tree

9 files changed

+237
-3
lines changed

9 files changed

+237
-3
lines changed

.github/workflows/node.js.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,22 @@ jobs:
8383
verbose: true
8484
file: ./coverage/coverage-final.json
8585
flags: windows-unittests
86+
87+
lint:
88+
name: Ubuntu nodejs Lint
89+
runs-on: ubuntu-latest
90+
strategy:
91+
matrix:
92+
node-version: [16.x]
93+
vscode-version: [stable]
94+
env:
95+
NODE_OPTIONS: '--max-old-space-size=8192'
96+
steps:
97+
- uses: actions/checkout@v3
98+
- name: Use Node.js ${{ matrix.node-version }}
99+
uses: actions/setup-node@v3
100+
with:
101+
node-version: ${{ matrix.node-version }}
102+
- run: npm ci
103+
- run: npm run testCompile
104+
- run: npm run lint

.vscode/launch.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,14 @@
137137
},
138138
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
139139
"preLaunchTask": "${defaultBuildTask}"
140+
},
141+
{
142+
"name": "Test Lint",
143+
"type": "node",
144+
"request": "launch",
145+
"program": "${workspaceFolder}/scripts/lint/testLint.ts",
146+
"outFiles": ["${workspaceFolder}/dist/**/*.js"],
147+
"preLaunchTask": "${defaultBuildTask}"
140148
}
141149
],
142150
"compounds": [

docs/TESTPLAN.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Ratio of unit to integ tests: 90% unit tests, 10% system/acceptance tests.
1515

1616
## Test categories
1717

18-
The test suite has two categories of tests:
18+
The test suite has the following categories of tests:
1919

2020
- Unit Tests: **fast** tests
2121
- Live in `src/test/`
@@ -27,6 +27,11 @@ The test suite has two categories of tests:
2727
- May use the filesystem.
2828
- Main property is that [the test is fast](https://pycon-2012-notes.readthedocs.io/en/latest/fast_tests_slow_tests.html).
2929
- Global state is shared across tests, thus there is a risk that later tests are polluted by earlier tests.
30+
- Lint Tests:
31+
- Live in `src/testLint`
32+
- Can run from CLI with `npm run lint`
33+
- Any type of test related to the format/quality/content of the code
34+
- Does not have context of the `vscode` api
3035
- Integration Tests: **slow** tests
3136
- Live in `src/integrationTest/`
3237
- Use a full instance of VSCode with an activated instance of the extension.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3431,7 +3431,7 @@
34313431
}
34323432
},
34333433
"scripts": {
3434-
"vscode:prepublish": "npm run clean && npm run buildScripts && npm run lint && webpack --mode production && npm run copyFiles -- --webpacked",
3434+
"vscode:prepublish": "npm run clean && npm run buildScripts && webpack --mode production && npm run copyFiles -- --webpacked",
34353435
"clean": "ts-node ./scripts/clean.ts dist",
34363436
"reset": "npm run clean -- node_modules && npm install",
34373437
"copyFiles": "ts-node ./scripts/build/copyFiles.ts",
@@ -3443,7 +3443,7 @@
34433443
"test": "npm run testCompile && ts-node ./scripts/test/test.ts && npm run report",
34443444
"testE2E": "npm run testCompile && ts-node ./scripts/test/testE2E.ts && npm run report",
34453445
"integrationTest": "npm run testCompile && ts-node ./scripts/test/integrationTest.ts && npm run report",
3446-
"lint": "eslint -c .eslintrc.js --ext .ts .",
3446+
"lint": "ts-node ./scripts/lint/testLint.ts",
34473447
"lintfix": "eslint -c .eslintrc.js --fix --ext .ts .",
34483448
"package": "ts-node ./scripts/build/package.ts",
34493449
"install-plugin": "vsce package -o aws-toolkit-vscode-test.vsix && code --install-extension aws-toolkit-vscode-test.vsix",

scripts/lint/testLint.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*!
2+
* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { glob } from 'glob'
7+
import * as Mocha from 'mocha'
8+
;(async () => {
9+
try {
10+
console.log('Running linting tests...')
11+
12+
const mocha = new Mocha()
13+
14+
const testFiles = glob.sync('dist/src/testLint/**/*.test.js')
15+
testFiles.forEach(file => {
16+
mocha.addFile(file)
17+
})
18+
19+
mocha.run(failures => {
20+
const exitCode = failures ? 1 : 0
21+
console.log(`Finished running Main test suite with result code: ${exitCode}`)
22+
process.exit(exitCode)
23+
})
24+
} catch (err) {
25+
console.error(err)
26+
console.error('Failed to run tests')
27+
process.exit(1)
28+
}
29+
})()

src/testLint/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Lint Tests
2+
3+
These are linting related tests that must pass in CI. Lint code is nodejs-only, they
4+
must not use the vscode API.

src/testLint/eslint.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*!
2+
* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import * as assert from 'assert'
7+
import { runCmd } from './testUtils'
8+
9+
describe('eslint', function () {
10+
this.timeout(180_000)
11+
12+
it('passes eslint', function () {
13+
const result = runCmd(['./node_modules/.bin/eslint', '-c', '.eslintrc.js', '--ext', '.ts', '.'], {
14+
throws: false,
15+
})
16+
assert.strictEqual(result.status, 0, result.error)
17+
})
18+
})

src/testLint/gitSecrets.test.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*!
2+
* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { describe } from 'mocha'
7+
8+
import * as assert from 'assert'
9+
import * as path from 'path'
10+
import { platform } from 'os'
11+
import { existsSync, mkdirSync, unlinkSync, writeFileSync } from 'fs'
12+
import { runCmd } from './testUtils'
13+
14+
/**
15+
* NOTES:
16+
* - git-secrets patterns are set in the project's `.git/config` file
17+
*/
18+
describe('git-secrets', function () {
19+
let accessKeyFilePath: string
20+
let toolkitProjectDir: string
21+
let testFixturesPath: string
22+
let gitSecrets: string // path to the git-secrets executable
23+
24+
/** git-secrets patterns that will not cause a failure during the scan */
25+
function setAllowListPatterns(gitSecrets: string) {
26+
const allowListPatterns: string[] = ['"accountId": "123456789012"']
27+
28+
allowListPatterns.forEach(pattern => {
29+
// Returns non-zero exit code if pattern already exists
30+
runCmd([gitSecrets, '--add', '--allowed', pattern], { cwd: toolkitProjectDir, throws: false })
31+
})
32+
}
33+
34+
function setDenyListPatterns(gitSecrets: string) {
35+
const denyListPatterns: string[] = []
36+
37+
denyListPatterns.forEach(pattern => {
38+
// Returns non-zero exit code if pattern already exists
39+
runCmd([gitSecrets, '--add', pattern], { cwd: toolkitProjectDir, throws: false })
40+
})
41+
}
42+
43+
function setupTestFixturesDir(toolkitProjectDir: string) {
44+
const testFixturesPath = path.join(toolkitProjectDir, 'src', 'testFixtures', 'bin')
45+
mkdirSync(testFixturesPath, { recursive: true })
46+
return testFixturesPath
47+
}
48+
49+
function setupAccessKeyFile(testFixturesPath: string) {
50+
const accessKeyFilePath = path.join(testFixturesPath, 'fileWithAccessKey.ts')
51+
deleteFileIfExists(accessKeyFilePath)
52+
return accessKeyFilePath
53+
}
54+
55+
function setupGitSecretsExecutable(testFixturesPath: string) {
56+
const gitSecretsExecutablePath = path.join(testFixturesPath, 'git-secrets')
57+
58+
if (existsSync(gitSecretsExecutablePath)) {
59+
console.log('INFO: git-secrets already installed')
60+
} else {
61+
console.log('INFO: Installing git-secrets...')
62+
runCmd(['mkdir', '-p', path.parse(gitSecretsExecutablePath).dir])
63+
runCmd([
64+
'curl',
65+
'-o',
66+
gitSecretsExecutablePath,
67+
'https://raw.githubusercontent.com/awslabs/git-secrets/99d01d58ebcc06e237c0e3f3ff5ae628aeef6aa6/git-secrets',
68+
])
69+
runCmd(['chmod', '+x', gitSecretsExecutablePath])
70+
}
71+
72+
return gitSecretsExecutablePath
73+
}
74+
75+
function deleteFileIfExists(filePath: string) {
76+
if (existsSync(filePath)) {
77+
unlinkSync(filePath)
78+
}
79+
}
80+
81+
function createFileWithSecretKey(accessKeyFilePath: string) {
82+
// Create file in project that has secret key value.
83+
// Need to build access key string incrementally to not trigger git-secrets.
84+
const keyValue = 'yAki21XLhAIBiKvyaxr4p/ltr8OxkZTHISISFAKE'
85+
const mySecretAccessKey = `const x = { "aws_secret_access_key": "${keyValue}" }`
86+
const fileContent = `
87+
/*!
88+
* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
89+
* SPDX-License-Identifier: Apache-2.0
90+
*/
91+
92+
${mySecretAccessKey}
93+
`.trim()
94+
writeFileSync(accessKeyFilePath, fileContent)
95+
}
96+
97+
before(function () {
98+
if (platform() === 'win32') {
99+
this.skip()
100+
}
101+
102+
toolkitProjectDir = path.resolve()
103+
testFixturesPath = setupTestFixturesDir(toolkitProjectDir)
104+
gitSecrets = setupGitSecretsExecutable(testFixturesPath)
105+
accessKeyFilePath = setupAccessKeyFile(testFixturesPath)
106+
107+
// Register all patterns with `git-secrets`
108+
runCmd([gitSecrets, '--register-aws'], { cwd: toolkitProjectDir })
109+
setDenyListPatterns(gitSecrets)
110+
setAllowListPatterns(gitSecrets)
111+
})
112+
113+
afterEach(function () {
114+
deleteFileIfExists(accessKeyFilePath)
115+
})
116+
117+
it('ensures no git secrets are found', function () {
118+
const result = runCmd([gitSecrets, '--scan'], { cwd: toolkitProjectDir })
119+
assert.strictEqual(result.status, 0, `Failure output: ${result.stderr.toString()}`)
120+
})
121+
122+
it('sanity check it finds secrets', function () {
123+
createFileWithSecretKey(accessKeyFilePath)
124+
const result = runCmd([gitSecrets, '--scan', accessKeyFilePath], { cwd: toolkitProjectDir, throws: false })
125+
assert.strictEqual(result.status, 1)
126+
})
127+
})

src/testLint/testUtils.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*!
2+
* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
import { SpawnSyncOptions, spawnSync } from 'child_process'
7+
8+
export function runCmd(args: string[], options?: SpawnSyncOptions & { throws?: boolean }) {
9+
const result = spawnSync(args[0], args.slice(1), options)
10+
11+
const throws = options?.throws ?? true
12+
if (throws && result.status !== 0) {
13+
throw new Error(`
14+
-----
15+
Error running: $ ${args.join(' ')}
16+
17+
status: ${result.status}
18+
error: ${result.error?.toString()}
19+
stdout: ${result.stdout?.toString()}
20+
stderr: ${result.stderr?.toString()}
21+
-----`)
22+
}
23+
return result
24+
}

0 commit comments

Comments
 (0)