Skip to content

Commit fe2767d

Browse files
committed
fix: resolve quality scan issues (iteration 4)
High-severity fixes: - Fix cache TOCTOU race in getOrFetch (remove redundant checks, set promise immediately) - Remove Node 20 from CI test matrix (align with package.json engines >=22) - Add missing imports to documentation example (glob, readFileUtf8) Medium-severity fixes: - Fix bash loop in security-checks.sh (use while IFS= read pattern for spaces) - Fix arrayChunk logic error (remove Math.min, use chunkSize directly) All core tests passing (6324/6328 tests, 4 unrelated archive test issues remain from iteration 3).
1 parent 03ec70b commit fe2767d

6 files changed

Lines changed: 20 additions & 23 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ on:
1919
description: 'Node.js versions to test (JSON array)'
2020
required: false
2121
type: string
22-
default: '["20", "22", "24"]'
22+
default: '["22", "24"]'
2323

2424
permissions:
2525
contents: read
@@ -33,7 +33,7 @@ jobs:
3333
lint-script: 'pnpm run lint --all'
3434
type-check-script: 'pnpm run check'
3535
test-script: 'pnpm exec vitest --config .config/vitest.config.mts run'
36-
node-versions: ${{ inputs.node-versions || '["20", "22", "24"]' }}
36+
node-versions: ${{ inputs.node-versions || '["22", "24"]' }}
3737
os-versions: '["ubuntu-latest", "windows-latest"]'
3838
fail-fast: false
3939
max-parallel: 4

.husky/security-checks.sh

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,19 +73,19 @@ done <<< "$STAGED_FILES"
7373

7474
# Check for Socket API keys.
7575
printf "Checking for API keys...\n"
76-
for file in $STAGED_FILES; do
76+
while IFS= read -r file; do
7777
if [ -f "$file" ]; then
7878
if grep -E 'sktsec_[a-zA-Z0-9_-]+' "$file" 2>/dev/null | grep -v "$ALLOWED_PUBLIC_KEY" | grep -v 'your_api_key_here' | grep -v 'SOCKET_SECURITY_API_KEY=' | grep -v 'fake-token' | grep -v 'test-token' | grep -q .; then
7979
printf "${YELLOW}⚠ WARNING: Potential API key found in: $file${NC}\n"
8080
grep -n 'sktsec_' "$file" | grep -v "$ALLOWED_PUBLIC_KEY" | grep -v 'your_api_key_here' | grep -v 'fake-token' | grep -v 'test-token' | head -3
8181
printf "If this is a real API key, DO NOT COMMIT IT.\n"
8282
fi
8383
fi
84-
done
84+
done <<< "$STAGED_FILES"
8585

8686
# Check for common secret patterns.
8787
printf "Checking for potential secrets...\n"
88-
for file in $STAGED_FILES; do
88+
while IFS= read -r file; do
8989
if [ -f "$file" ]; then
9090
# Skip test files, example files, and hook scripts.
9191
if echo "$file" | grep -qE '\.(test|spec)\.(m?[jt]s|tsx?)$|\.example$|/test/|/tests/|fixtures/|\.git-hooks/|\.husky/'; then
@@ -112,7 +112,7 @@ for file in $STAGED_FILES; do
112112
ERRORS=$((ERRORS + 1))
113113
fi
114114
fi
115-
done
115+
done <<< "$STAGED_FILES"
116116

117117
if [ $ERRORS -gt 0 ]; then
118118
printf "\n"

docs/examples.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,10 @@ Execute tasks with limited concurrency:
448448
```typescript
449449
import fs from 'node:fs/promises'
450450

451+
// Note: fast-glob is an external dependency - install it separately (pnpm add -D fast-glob)
452+
import { glob } from 'fast-glob'
453+
454+
import { readFileUtf8 } from '@socketsecurity/lib/fs'
451455
import { PromiseQueue } from '@socketsecurity/lib/promise-queue'
452456
import { Spinner } from '@socketsecurity/lib/spinner'
453457
import { getDefaultLogger } from '@socketsecurity/lib/logger'

pnpm-lock.yaml

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/arrays.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,9 @@ export function arrayChunk<T>(
104104
throw new Error('Chunk size must be greater than 0')
105105
}
106106
const { length } = arr
107-
const actualChunkSize = Math.min(length, chunkSize)
108107
const chunks = []
109-
for (let i = 0; i < length; i += actualChunkSize) {
110-
chunks.push(arr.slice(i, i + actualChunkSize) as T[])
108+
for (let i = 0; i < length; i += chunkSize) {
109+
chunks.push(arr.slice(i, i + chunkSize) as T[])
111110
}
112111
return chunks
113112
}

src/cache-with-ttl.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -407,34 +407,28 @@ export function createTtlCache(options?: TtlCacheOptions): TtlCache {
407407

408408
const fullKey = buildKey(key)
409409

410-
// Atomic check-and-set to prevent TOCTOU race
410+
// Check if another request is already in flight
411411
const existing = inflightRequests.get(fullKey)
412412
if (existing) {
413413
return await existing
414414
}
415415

416-
// Create and immediately store promise atomically
416+
// Create promise with cleanup handlers
417417
const promise = (async () => {
418418
try {
419419
const data = await fetcher()
420420
await set(key, data)
421421
return data
422-
} catch (error) {
423-
// Clean up on error
424-
inflightRequests.delete(fullKey)
425-
throw error
426422
} finally {
423+
// Clean up on both success and error
427424
inflightRequests.delete(fullKey)
428425
}
429426
})()
430427

431-
// Final check - if another thread won, use theirs
432-
const nowExisting = inflightRequests.get(fullKey)
433-
if (nowExisting && nowExisting !== promise) {
434-
return await nowExisting
435-
}
436-
428+
// Set the promise IMMEDIATELY before any await to prevent race
437429
inflightRequests.set(fullKey, promise)
430+
431+
// Await and return (cleanup happens in finally block)
438432
return await promise
439433
}
440434

0 commit comments

Comments
 (0)