Skip to content

Commit 351c47c

Browse files
committed
fix: track per-repo attempted tokens and match HTTP status by word boundary
Signed-off-by: Joana Maia <jmaia@contractor.linuxfoundation.org>
1 parent c5c8646 commit 351c47c

2 files changed

Lines changed: 30 additions & 11 deletions

File tree

services/apps/security_best_practices_worker/src/activities/index.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,10 @@ function classifyTokenError(output: string, source: string): void {
203203
})
204204
}
205205

206-
const has403 = output.includes('403')
207-
const has429 = output.includes('429')
206+
// Word-boundary match so unrelated numbers (e.g. "processed 4291 records") don't get
207+
// misclassified as HTTP 429/403.
208+
const has403 = /\b403\b/.test(output)
209+
const has429 = /\b429\b/.test(output)
208210
if (!has403 && !has429) return
209211

210212
// Rate-limit responses carry a distinctive body: "API rate limit exceeded" or

services/apps/security_best_practices_worker/src/workflows/triggerSecurityInsightsCheckForRepos.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,23 +70,33 @@ export async function triggerSecurityInsightsCheckForRepos(
7070
// via `!tokenInfo`, so requeue decisions have to be made here too — otherwise a rate-limit
7171
// wave would falsely fail the repo for the rest of the continueAsNew chain.
7272
let sawRateLimit = false
73+
// Per-repo attempted-tokens set. TokenRepoAccessError releases the token in "clean"
74+
// global state, so without this filter `getNextToken` could re-pick the same token
75+
// while the others are held by concurrent children — burning attempts without ever
76+
// reaching a token that might have access.
77+
const attemptedTokens = new Set<string>()
7378
while (attempts < MAX_TOKEN_ATTEMPTS) {
74-
const tokenInfo = getNextToken(tokenInfos, currentTimeMs)
79+
const tokenInfo = getNextToken(tokenInfos, currentTimeMs, attemptedTokens)
7580
if (!tokenInfo) {
76-
// Only mark the repo failed if every token is permanently invalid. Rate-limited
77-
// tokens recover after 1h and in-use tokens free up as concurrent tasks finish, so
78-
// in both cases requeue the repo — the outer loop's hasFreeToken gate + defer path
79-
// handles the wait.
81+
// No untried, usable token remains for this repo. Distinguish two cases:
82+
// - all tokens permanently invalid, OR every non-invalid token has been tried for
83+
// this repo → this repo genuinely can't be scanned, fail it
84+
// - some non-invalid, non-attempted tokens exist but are currently in-use or
85+
// rate-limited → requeue so a later iteration can retry when they free up
8086
const allPermanentlyInvalid = tokenInfos.every((t) => t.isInvalid)
81-
if (allPermanentlyInvalid) {
82-
console.error(`All tokens permanently invalid for repo ${repo.repoUrl}, skipping`)
87+
const anyUnattemptedNonInvalid = tokenInfos.some(
88+
(t) => !t.isInvalid && !attemptedTokens.has(t.token),
89+
)
90+
if (allPermanentlyInvalid || !anyUnattemptedNonInvalid) {
91+
console.error(`No untried tokens remain for repo ${repo.repoUrl}, skipping`)
8392
failedRepoUrls.push(repo.repoUrl)
8493
} else {
8594
queue.unshift(repo)
8695
}
8796
break
8897
}
8998
const token = tokenInfo.token
99+
attemptedTokens.add(token)
90100
tokenInfo.inUse = true
91101

92102
try {
@@ -256,10 +266,17 @@ function refreshExpiredRateLimits(tokenInfos: ITokenInfo[], nowMs: number): void
256266
}
257267
}
258268

259-
function getNextToken(tokenInfos: ITokenInfo[], nowMs: number): ITokenInfo | null {
269+
function getNextToken(
270+
tokenInfos: ITokenInfo[],
271+
nowMs: number,
272+
excludeTokens?: Set<string>,
273+
): ITokenInfo | null {
260274
refreshExpiredRateLimits(tokenInfos, nowMs)
261275

262-
const usableTokenInfos = tokenInfos.filter((t) => !t.inUse && !t.isRateLimited && !t.isInvalid)
276+
const usableTokenInfos = tokenInfos.filter(
277+
(t) =>
278+
!t.inUse && !t.isRateLimited && !t.isInvalid && !(excludeTokens && excludeTokens.has(t.token)),
279+
)
263280

264281
// sort usable tokens by last used date from oldest to newest
265282
const sortedTokenInfos = usableTokenInfos.sort((a, b) => {

0 commit comments

Comments
 (0)