-
-
Notifications
You must be signed in to change notification settings - Fork 81
Description
Describe the bug
In cornucopia.owasp.org/src/lib/services/deckService.ts, the getCardData() method calls DeckService.cache.push(...) inside the loop that iterates over editions. This means the static cache is populated with a partial result (mobileapp cards only) before the full merged result (mobileapp + webapp) is pushed. Since cache.find() in getCards() returns the first matching entry, every subsequent call to getCards(lang) returns mobileapp-only cards, silently omitting all webapp cards.
Affected file
cornucopia.owasp.org/src/lib/services/deckService.ts — getCardData() method (lines 66–73)
Code snippet (problematic section)
private getCardData(lang: string) {
let cards = new Map<string, Card>;
const decks = DeckService.latests; // [mobileapp 1.1, webapp 2.2]
for (let i in decks) {
cards = new Map([...this.getCardDataForEditionVersionLang(decks[i].edition, decks[i].version, lang), ...cards]);
DeckService.cache.push({ lang: lang, data: cards, version: 'latest' }); // ← BUG: inside loop
}
return cards;
}
public getCards(lang: string): Map<string, Card> {
return DeckService.cache.find((deck) => deck?.lang == lang && deck?.version == 'latest')?.data
|| this.getCardData(lang);
}Step-by-step execution trace
| Iteration | decks[i] |
cards after merge |
Entry pushed to cache |
|---|---|---|---|
i = 0 |
mobileapp 1.1 | mobileapp cards only | { lang, 'latest', mobileapp-only } ← first match |
i = 1 |
webapp 2.2 | mobileapp + webapp merged | { lang, 'latest', full merged } |
After the first call to getCardData('en'):
cache[0]= mobileapp cards onlycache[1]= mobileapp + webapp merged
Any subsequent call to getCards('en'):
cache.find(d => d.lang == 'en' && d.version == 'latest')returnscache[0](first match)- Returns mobileapp-only data — webapp cards are completely missing
Expected behavior
cache.push must be placed outside the loop so the cache is only populated with the fully-merged result:
private getCardData(lang: string) {
let cards = new Map<string, Card>;
const decks = DeckService.latests;
for (let i in decks) {
cards = new Map([...this.getCardDataForEditionVersionLang(decks[i].edition, decks[i].version, lang), ...cards]);
}
DeckService.cache.push({ lang: lang, data: cards, version: 'latest' }); // ← moved outside loop
return cards;
}Impact
- The
/cardspage on cornucopia.owasp.org relies ongetCards()to populate the full card browser. - After the first page load (which warms the cache), all subsequent calls return only mobileapp cards.
- Webapp cards (the primary edition) are silently absent from the card browser for all users until the server restarts.
Steps to reproduce
- Load a page that calls
DeckService.getCards(lang)twice (e.g., navigate to/cards, then navigate away and back). - On the second load,
getCardshits the cache and returns only mobileapp cards. - Webapp cards (
VE2,AT5, etc.) are absent from the results.
Additional context
DeckService.cacheis aprivate staticfield — a class-level singleton shared across all instances and requests.- The bug does not affect direct calls to
getCardDataForEditionVersionLang(), only calls going throughgetCards()after the cache is warmed.