Skip to content

Commit a976d15

Browse files
authored
feat: restrict requirements for enhanced secret scan matches (#6379)
1 parent 9f063f8 commit a976d15

File tree

2 files changed

+28
-40
lines changed
  • packages/build

2 files changed

+28
-40
lines changed

packages/build/src/plugins_core/secrets_scanning/utils.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -130,19 +130,19 @@ const MIN_CHARS_AFTER_PREFIX = 12
130130
const prefixMatchingRegex = LIKELY_SECRET_PREFIXES.map((p) => p.replace(/[$*+?.()|[\]{}]/g, '\\$&')).join('|')
131131

132132
// Build regex pattern for matching secrets with various delimiters and quotes:
133-
// (?:["'`]|^|[=:,]) - match either quotes, start of line, or delimiters (=:,) at the start
133+
// (?:["'\`]|[=]) - match either quotes, or = at the start
134134
// Named capturing groups:
135135
// - <token>: captures the entire secret value including its prefix
136136
// - <prefix>: captures just the prefix part (e.g. 'aws_', 'github_pat_')
137137
// (?:${prefixMatchingRegex}) - non-capturing group containing our escaped prefixes (e.g. aws_|github_pat_|etc)
138-
// [^ "'`=:,]{${MIN_CHARS_AFTER_PREFIX}} - match exactly MIN_CHARS_AFTER_PREFIX chars after the prefix
139-
// [^ "'`=:,]*? - lazily match any additional chars that aren't quotes/delimiters
140-
// (?:["'`]|[ =:,]|$) - end with either quotes, delimiters, whitespace, or end of line
138+
// [a-zA-Z0-9-]{${MIN_CHARS_AFTER_PREFIX}} - match exactly MIN_CHARS_AFTER_PREFIX chars (alphanumeric or dash) after the prefix
139+
// [a-zA-Z0-9-]*? - lazily match any additional chars (alphanumeric or dash)
140+
// (?:["'\`]|$) - end with either quotes or end of line
141141
// gi - global and case insensitive flags
142142
// Note: Using the global flag (g) means this regex object maintains state between executions.
143143
// We would need to reset lastIndex to 0 if we wanted to reuse it on the same string multiple times.
144144
const likelySecretRegex = new RegExp(
145-
`(?:["'\`]|^|[=:,]) *(?<token>(?<prefix>${prefixMatchingRegex})[^ "'\`=:,]{${MIN_CHARS_AFTER_PREFIX}}[^ "'\`=:,]*?)(?:["'\`]|[ =:,]|$)`,
145+
`(?:["'\`]|[=]) *(?<token>(?<prefix>${prefixMatchingRegex})[a-zA-Z0-9-]{${MIN_CHARS_AFTER_PREFIX}}[a-zA-Z0-9-]*?)(?:["'\`]|$)`,
146146
'gi',
147147
)
148148

packages/build/tests/utils_secretscanning/tests.js

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ import test from 'ava'
22

33
import { findLikelySecrets } from '../../lib/plugins_core/secrets_scanning/utils.js'
44

5-
const testFile = 'test.txt'
6-
7-
test('findLikelySecrets - should find secrets with common prefixes at the beginning of a line', async (t) => {
5+
test('findLikelySecrets - should not find secrets without quotes or delimiters', async (t) => {
86
const lines = [
97
'aws_123456789012345678',
108
'ghp_1234567890123456789',
@@ -14,29 +12,22 @@ test('findLikelySecrets - should find secrets with common prefixes at the beginn
1412

1513
lines.forEach((text) => {
1614
const matches = findLikelySecrets({ text })
17-
t.is(matches.length, 1)
18-
t.like(matches[0], {
19-
// match found at the beginning of the line
20-
index: 0,
21-
})
15+
t.is(matches.length, 0)
2216
})
2317
})
2418

25-
test('findLikelySecrets - should find secrets with various delimiters at the beginning', async (t) => {
19+
test('findLikelySecrets - should find secrets with quotes or equals', async (t) => {
2620
const matchingLines = [
2721
'my_secret_key=aws_123456789012345678',
28-
'awsKey: aws_123456789012345678',
2922
'mySecretKey = aws_123456789012345678',
3023
'secretKey="aws_123456789012345678"',
24+
'secretKey = "aws_123456789012345678"',
3125
"secretKey='aws_123456789012345678'",
3226
'secretKey=`aws_123456789012345678`',
33-
'someKey, aws_123456789012345678, otherKey',
3427
]
3528
matchingLines.forEach((text) => {
3629
const matches = findLikelySecrets({ text })
3730
t.is(matches.length, 1)
38-
39-
t.true(matches[0].index > 0, 'Match should not be at the start of the line')
4031
})
4132
})
4233

@@ -47,12 +38,12 @@ test('findLikelySecrets - should not match values with spaces after prefix', asy
4738
})
4839

4940
test('findLikelySecrets - should not match values that are too short', async (t) => {
50-
const matches = findLikelySecrets({ text: 'aws_key=12345678901' })
41+
const matches = findLikelySecrets({ text: 'aws_key="12345678901"' })
5142
t.is(matches.length, 0)
5243
})
5344

5445
test('findLikelySecrets - should return the matched prefix as the key', async (t) => {
55-
const matches = findLikelySecrets({ text: 'github_pat_123456789012345678' })
46+
const matches = findLikelySecrets({ text: 'mykey = "github_pat_123456789012345678"' })
5647
t.is(matches.length, 1)
5748
t.is(matches[0].prefix, 'github_pat_')
5849
})
@@ -67,17 +58,13 @@ test('findLikelySecrets - should handle empty or invalid input', async (t) => {
6758
})
6859

6960
test('findLikelySecrets - should match exactly minimum chars after prefix', async (t) => {
70-
const exactMinChars = 'aws_123456789012' // Exactly 12 chars after prefix
61+
const exactMinChars = 'value = "aws_123456789012"' // Exactly 12 chars after prefix
7162
const matches = findLikelySecrets({ text: exactMinChars })
7263
t.is(matches.length, 1)
7364
})
7465

7566
test('findLikelySecrets - should match different prefixes from LIKELY_SECRET_PREFIXES', async (t) => {
76-
const lines = [
77-
'ghp_123456789012345678', // GitHub personal access token
78-
'sk_live_123456789012345678', // Stripe key
79-
'AKIAXXXXXXXXXXXXXXXX', // AWS access key
80-
]
67+
const lines = ['key="ghp_123456789012345678"', 'key="sk_123456789012345678"', 'key="aws_123456789012345678"']
8168

8269
lines.forEach((text) => {
8370
const matches = findLikelySecrets({ text })
@@ -91,34 +78,35 @@ test('findLikelySecrets - should skip safe-listed values', async (t) => {
9178
t.is(matches.length, 0)
9279
})
9380

94-
test('findLikelySecrets - should match secrets with special characters', async (t) => {
95-
const lines = [
96-
'aws_abc123!@#$%^&*()_+', // Special chars
97-
'ghp_abc-123_456.789', // Common separator chars
98-
'sk_live_123-456_789.000', // Mix of numbers and separators
99-
]
81+
test('findLikelySecrets - should allow dashes and alphanumeric characters only', async (t) => {
82+
const validLines = ['key="aws_abc123-456-789"', 'key="ghp_abc-123-def-456"']
10083

101-
lines.forEach((text) => {
102-
const matches = findLikelySecrets({ text })
103-
t.is(matches.length, 1)
84+
validLines.forEach((line) => {
85+
const matches = findLikelySecrets({ text: line })
86+
t.is(matches.length, 1, `Should match line with dashes: ${line}`)
87+
})
88+
89+
const invalidLines = ['key="aws_abc123!@#$%^&*()_+"', 'key="ghp_abc.123_456.789"', 'key="sk_live_123_456_789"']
90+
91+
invalidLines.forEach((line) => {
92+
const matches = findLikelySecrets({ text: line })
93+
t.is(matches.length, 0, `Should not match line with special characters: ${line}`)
10494
})
10595
})
10696

10797
test('findLikelySecrets - should match full secret value against omitValues', async (t) => {
10898
// Test both partial and full matches to ensure proper behavior
10999
const partialMatch = findLikelySecrets({
110-
text: 'aws_123456789012_extra_chars_here',
100+
text: 'key="aws_123456789012extracharshere"',
111101
// The omitValue only partially matches the secret - we should still detect the secret
112102
omitValuesFromEnhancedScan: ['aws_123456789012'],
113103
})
114104
t.is(partialMatch.length, 1)
115105

116106
const fullMatch = findLikelySecrets({
117-
line: 'aws_123456789012_extra_chars_here',
118-
file: testFile,
119-
lineNumber: 1,
107+
text: 'key="aws_123456789012extracharshere"',
120108
// Omit the full secret value - we should not detect the secret
121-
omitValuesFromEnhancedScan: ['aws_123456789012_extra_chars_here'],
109+
omitValuesFromEnhancedScan: ['aws_123456789012extracharshere'],
122110
})
123111
t.is(fullMatch.length, 0)
124112
})

0 commit comments

Comments
 (0)