-
-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: kriptonian1 <[email protected]>
- Loading branch information
1 parent
af06071
commit 85cb8ab
Showing
88 changed files
with
6,627 additions
and
1,801 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,179 @@ | ||
import BaseCommand from '@/commands/base.command' | ||
import type { | ||
CommandActionData, | ||
CommandOption | ||
} from '@/types/command/command.types' | ||
import { execSync } from 'child_process' | ||
import { readFileSync, statSync } from 'fs' | ||
import { globSync } from 'glob' | ||
import path from 'path' | ||
import secretDetector from 'secret-scan' | ||
// eslint-disable-next-line @typescript-eslint/no-var-requires | ||
const colors = require('colors/safe') | ||
|
||
export default class ScanCommand extends BaseCommand { | ||
getOptions(): CommandOption[] { | ||
return [ | ||
{ | ||
short: '-f', | ||
long: '--file <string>', | ||
description: 'Scan a specific file' | ||
}, | ||
{ | ||
short: '-c', | ||
long: '--current-changes', | ||
description: | ||
'Scan only the current changed files that are not committed' | ||
} | ||
] | ||
} | ||
|
||
getName(): string { | ||
return 'scan' | ||
} | ||
|
||
getDescription(): string { | ||
return 'Scan the project to detect any hardcoded secrets' | ||
} | ||
|
||
action({ options }: CommandActionData): Promise<void> | void { | ||
const { currentChanges, file } = options | ||
|
||
console.log('\n=============================') | ||
console.log(colors.cyan.bold('🔍 Secret Scan Started')) | ||
console.log('=============================') | ||
|
||
if (file) { | ||
console.log(`Scanning file: ${file}`) | ||
this.scanFiles([file as string]) | ||
return | ||
} | ||
if (currentChanges) { | ||
console.log('Scanning only the current changes') | ||
const files = this.getChangedFiles() | ||
this.scanFiles(files) | ||
} else { | ||
console.log('\n\n📂 Scanning all files...\n') | ||
const files = this.getAllFiles() | ||
this.scanFiles(files) | ||
} | ||
} | ||
|
||
private scanFiles(allfiles: string[]) { | ||
const foundSecrets = [] | ||
let skipNextLine = false | ||
for (const file of allfiles) { | ||
const stats = statSync(file) | ||
if (stats.isFile()) { | ||
const content = readFileSync(file, 'utf8').split(/\r?\n/) | ||
|
||
// Skip the file if ignore comment is found in the first line | ||
if (content[0].includes('keyshade-ignore-all')) { | ||
continue | ||
} | ||
|
||
content.forEach((line, index) => { | ||
// Skip the next line if ignore comment is found in the previous line | ||
if (skipNextLine) { | ||
skipNextLine = false | ||
return | ||
} | ||
|
||
if (line.includes('keyshade-ignore')) { | ||
skipNextLine = true | ||
return | ||
} | ||
const { found, regex } = secretDetector.detect(line) | ||
if (found) { | ||
const matched = line.match(regex) | ||
const highlightedLine = line | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument | ||
.replace(regex, colors.red.underline(matched[0]) as string) | ||
.trim() | ||
foundSecrets.push({ | ||
file, | ||
line: index + 1, | ||
content: highlightedLine | ||
}) | ||
} | ||
}) | ||
} | ||
} | ||
if (foundSecrets.length > 0) { | ||
console.log( | ||
colors.red(`\n 🚨 Found ${foundSecrets.length} hardcoded secrets:\n`) | ||
) | ||
foundSecrets.forEach((secret) => { | ||
console.log( | ||
`${colors.underline(`${secret.file}:${secret.line}`)}: ${secret.content}` | ||
) | ||
console.log( | ||
colors.yellow( | ||
'Suggestion: Replace with environment variables or secure storage solutions.\n' | ||
) | ||
) | ||
}) | ||
console.log('=============================') | ||
console.log('Summary:') | ||
console.log('=============================') | ||
console.log(`🚨 Total Secrets Found: ${foundSecrets.length}\n`) | ||
|
||
process.exit(1) | ||
} else { | ||
console.log('=============================') | ||
console.log('Summary:') | ||
console.log('=============================') | ||
console.log('✅ Total Secrets Found: 0\n') | ||
console.log(colors.green('No hardcoded secrets found.')) | ||
} | ||
} | ||
|
||
private getAllFiles(): string[] { | ||
const currentWorkDir = process.cwd() | ||
let gitIgnorePatterns: string[] = [] | ||
try { | ||
const gitIgnorePath = path.resolve(currentWorkDir, '.gitignore') | ||
|
||
const gitIgnoreContent = readFileSync(gitIgnorePath, 'utf8') | ||
|
||
gitIgnorePatterns = gitIgnoreContent | ||
.split('\n') | ||
.filter((line) => line.trim() !== '' && !line.startsWith('#')) | ||
} catch (error) { | ||
console.log("Repository doesn't have .gitignore file") | ||
} | ||
|
||
return globSync(currentWorkDir + '/**/**', { | ||
dot: true, | ||
ignore: { | ||
ignored: (p) => { | ||
return gitIgnorePatterns.some((pattern) => { | ||
return p.isNamed(pattern) | ||
}) | ||
}, | ||
childrenIgnored: (p) => { | ||
return gitIgnorePatterns.some((pattern) => { | ||
return p.isNamed(pattern) | ||
}) | ||
} | ||
} | ||
}) | ||
} | ||
|
||
private getChangedFiles(): string[] { | ||
const output = execSync('git status -s').toString() | ||
const files = output | ||
.split('\n') | ||
.filter((line) => { | ||
if (typeof line === 'undefined') { | ||
return false | ||
} | ||
return line | ||
}) | ||
.map((line) => { | ||
line = line.trim().split(' ')[1] | ||
return path.resolve(process.cwd(), line) | ||
}) | ||
return files | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import type { Options } from 'tsup' | ||
import { defineConfig } from 'tsup' | ||
|
||
const env = process.env.NODE_ENV | ||
|
||
export default defineConfig((options: Options) => ({ | ||
plugins: [], | ||
treeshake: true, | ||
splitting: true, | ||
entryPoints: ['src/index.ts'], | ||
dts: true, | ||
minify: env === 'production', | ||
clean: true, | ||
...options | ||
})) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
module.exports = { | ||
parser: '@typescript-eslint/parser', | ||
parserOptions: { | ||
project: 'tsconfig.json', | ||
tsconfigRootDir: __dirname, | ||
sourceType: 'module' | ||
}, | ||
plugins: ['@typescript-eslint/eslint-plugin'], | ||
extends: [ | ||
'plugin:@typescript-eslint/recommended', | ||
'plugin:prettier/recommended', | ||
'standard-with-typescript' | ||
], | ||
root: true, | ||
env: { | ||
node: true, | ||
jest: true | ||
}, | ||
ignorePatterns: ['.eslintrc.js'], | ||
rules: { | ||
'@typescript-eslint/interface-name-prefix': 'off', | ||
'@typescript-eslint/explicit-function-return-type': 'off', | ||
'@typescript-eslint/explicit-module-boundary-types': 'off', | ||
'@typescript-eslint/no-explicit-any': 'off', | ||
'@typescript-eslint/no-unused-vars': ['warn'], | ||
'@typescript-eslint/space-before-function-paren': 'off', | ||
'@typescript-eslint/strict-boolean-expressions': 'off', | ||
'@typescript-eslint/prefer-nullish-coalescing': 'off', | ||
'space-before-function-paren': 'off', | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"require": "tsx", | ||
"spec": "src/test/**/*.ts", | ||
"extension": ["ts"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
{ | ||
"$schema": "https://json.schemastore.org/swcrc", | ||
"jsc": { | ||
"parser": { | ||
"syntax": "typescript", | ||
"tsx": false, | ||
"dynamicImport": true, | ||
"decorators": false | ||
}, | ||
"transform": null, | ||
"target": "es2022", | ||
"loose": false, | ||
"externalHelpers": false, | ||
"keepClassNames": false, | ||
"baseUrl": ".", | ||
"paths": { | ||
"@/*": ["./src/*"] | ||
} | ||
}, | ||
"module": { | ||
"type": "commonjs", | ||
"strict": false, | ||
"strictMode": false, | ||
"noInterop": false, | ||
"lazy": false | ||
}, | ||
"minify": true | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
{ | ||
"name": "secret-scan", | ||
"version": "1.0.0", | ||
"description": "Do static analysis of a string to find secrets", | ||
"main": "dist/index.js", | ||
"scripts": { | ||
"genKey": "tsx src/generateKey.ts", | ||
"build": "tsup", | ||
"test": "mocha" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"exports": { | ||
".": "./dist/index.js" | ||
}, | ||
"dependencies": { | ||
"mocha": "^10.6.0", | ||
"randexp": "^0.5.3", | ||
"tsx": "^4.16.2" | ||
}, | ||
"devDependencies": { | ||
"@types/mocha": "^10.0.7", | ||
"@types/node": "^20.14.10", | ||
"eslint-config-standard-with-typescript": "^43.0.1", | ||
"jest": "^29.5.0", | ||
"tsup": "^8.1.2", | ||
"typescript": "^5.5.3" | ||
} | ||
} |
Oops, something went wrong.