mirror of https://github.com/garrytan/gstack.git
fix(redact): fully-braced ${...} interpolations are code, whatever they contain
The identifier-only braced form flagged the DSN builder's own
${encodeURIComponent(dbPass)} call site as a pushed secret — a scan
that cries wolf on the fix for the previous finding. Any ${...}
spanning the whole password segment is template code; bare $word
stays uppercase-only so $hunter2 still blocks. The mismatched-brace
negative fixture assembles at runtime so this file's own pushed bytes
carry no blockable URL shape.
This commit is contained in:
parent
7726d25396
commit
09a50b5203
|
|
@ -263,7 +263,11 @@ export function insideUuid(match: RegExpExecArray): boolean {
|
||||||
* Shared by db.url_with_password and creds.basic_auth_url so the two
|
* Shared by db.url_with_password and creds.basic_auth_url so the two
|
||||||
* validators cannot drift.
|
* validators cannot drift.
|
||||||
*/
|
*/
|
||||||
const INTERPOLATED_PASSWORD_RE = /^(\$\{[A-Za-z_][A-Za-z0-9_]*\}|\$[A-Z_][A-Z0-9_]*)$/;
|
// Fully-braced `${...}` spanning the whole password segment is template code
|
||||||
|
// regardless of content — `${dbPass}` and `${encodeURIComponent(dbPass)}`
|
||||||
|
// alike (the identifier-only form flagged the DSN-encoding call site as a
|
||||||
|
// pushed secret). Bare `$word` stays uppercase-only: `$hunter2` must block.
|
||||||
|
const INTERPOLATED_PASSWORD_RE = /^(\$\{.+\}|\$[A-Z_][A-Z0-9_]*)$/;
|
||||||
function urlPasswordIsPlaceholder(span: string): boolean {
|
function urlPasswordIsPlaceholder(span: string): boolean {
|
||||||
const m = span.match(/:\/\/[^:]+:([^@]+)@/);
|
const m = span.match(/:\/\/[^:]+:([^@]+)@/);
|
||||||
const pw = m?.[1] ?? "";
|
const pw = m?.[1] ?? "";
|
||||||
|
|
|
||||||
|
|
@ -119,8 +119,13 @@ describe("HIGH credential patterns", () => {
|
||||||
// still block (both-braces-optional would have let it through).
|
// still block (both-braces-optional would have let it through).
|
||||||
expect(ids("postgres://user:$DB_PASSWORD@host/app")).not.toContain("db.url_with_password");
|
expect(ids("postgres://user:$DB_PASSWORD@host/app")).not.toContain("db.url_with_password");
|
||||||
expect(ids("postgres://admin:$" + "hun" + "ter2@db.internal/app")).toContain("db.url_with_password");
|
expect(ids("postgres://admin:$" + "hun" + "ter2@db.internal/app")).toContain("db.url_with_password");
|
||||||
// Mismatched brace is not an interpolation either.
|
// Mismatched brace is not an interpolation either (assembled at runtime
|
||||||
expect(ids("postgres://admin:${dbPass@db.internal/app")).toContain("db.url_with_password");
|
// so this file's own pushed bytes carry no blockable URL shape).
|
||||||
|
expect(ids("postgres://admin:${" + "dbPass@db.internal/app")).toContain("db.url_with_password");
|
||||||
|
// A fully-braced interpolation is code whatever it contains — the DSN
|
||||||
|
// builder's `${encodeURIComponent(dbPass)}` call site must not scan as a
|
||||||
|
// pushed secret.
|
||||||
|
expect(ids("postgresql://user:${encodeURIComponent(dbPass)}@host:5432/db")).not.toContain("db.url_with_password");
|
||||||
// A LOWERCASE literal 'password'/'pass' at the URL-password position is a
|
// A LOWERCASE literal 'password'/'pass' at the URL-password position is a
|
||||||
// real (terrible) credential, not a doc placeholder — only the ALL-CAPS
|
// real (terrible) credential, not a doc placeholder — only the ALL-CAPS
|
||||||
// doc convention (USER:PASSWORD) is suppressed. Assembled at runtime so
|
// doc convention (USER:PASSWORD) is suppressed. Assembled at runtime so
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue