This repository has been archived by the owner on Apr 29, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Find insecure comparison of secrets
- Loading branch information
Showing
8 changed files
with
23,522 additions
and
28 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { Event } from '@appland/models'; | ||
import { emptyValue, verbose } from '../scanner/util'; | ||
|
||
export default function (secrets: Set<string>, e: Event): void { | ||
if (emptyValue(e.returnValue.value)) { | ||
return; | ||
} | ||
if (verbose()) { | ||
console.warn(`Secret generated: ${e.returnValue.value}`); | ||
} | ||
secrets.add(e.returnValue.value); | ||
} |
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,17 @@ | ||
import { readFileSync } from 'fs'; | ||
import { join } from 'path'; | ||
// eslint-disable-next-line @typescript-eslint/no-unused-vars | ||
import * as _secretsRegexes from './secretsRegexesData.json'; // import directly to include json file into the build | ||
|
||
const regexData: { [key: string]: string | string[] } = JSON.parse( | ||
readFileSync(join(__dirname, 'secretsRegexesData.json')).toString() | ||
); | ||
|
||
const REGEXES: { [key: string]: RegExp[] } = Object.keys(regexData).reduce((memo, key) => { | ||
const value = regexData[key]; | ||
const regexes = Array.isArray(value) ? value : [value]; | ||
memo[key] = regexes.map((regex) => new RegExp(regex)); | ||
return memo; | ||
}, {} as { [key: string]: RegExp[] }); | ||
|
||
export default REGEXES; |
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,59 @@ | ||
import { Event } from '@appland/models'; | ||
import recordSecrets from '../analyzer/recordSecrets'; | ||
import SecretsRegexes from '../analyzer/secretsRegexes'; | ||
import Assertion from '../assertion'; | ||
|
||
const BCRYPT_REGEXP = /^[$]2[abxy]?[$](?:0[4-9]|[12][0-9]|3[01])[$][./0-9a-zA-Z]{53}$/; | ||
|
||
const secrets: Set<string> = new Set(); | ||
|
||
function stringEquals(e: Event): string | boolean | import('../types').MatchResult[] | undefined { | ||
if (!e.parameters || !e.receiver || e.parameters!.length !== 1) { | ||
return; | ||
} | ||
|
||
const args = [e.receiver!.value, e.parameters![0].value]; | ||
|
||
function isBcrypt(str: string): boolean { | ||
return BCRYPT_REGEXP.test(str); | ||
} | ||
|
||
function isSecret(str: string): boolean { | ||
return !!Object.keys(SecretsRegexes).find( | ||
(key): boolean => !!SecretsRegexes[key].find((re: RegExp): boolean => re.test(str)) | ||
); | ||
} | ||
|
||
// BCrypted strings are safe to compare using equals() | ||
if (args.every(isBcrypt)) { | ||
return; | ||
} | ||
if (!args.every(isSecret)) { | ||
return; | ||
} | ||
|
||
return true; | ||
} | ||
|
||
const scanner = function (): Assertion { | ||
return Assertion.assert( | ||
'insecure-compare', | ||
'Insecure comparison of secrets', | ||
'event', | ||
(e: Event) => { | ||
if (e.codeObject.labels.has('secret')) { | ||
recordSecrets(secrets, e); | ||
} | ||
if (e.parameters && e.codeObject.labels.has('string.equals')) { | ||
return stringEquals(e); | ||
} | ||
}, | ||
(assertion: Assertion): void => { | ||
assertion.where = (e: Event) => | ||
e.codeObject.labels.has('string.equals') || e.codeObject.labels.has('secret'); | ||
assertion.description = `Insecure comparison of secrets`; | ||
} | ||
); | ||
}; | ||
|
||
export default { scanner }; |
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
Oops, something went wrong.