Eop card browser/m2 eop deck browsing#3181
Conversation
…as in source files
…witch as a default card
…d its children ([version]/[lang])
There was a problem hiding this comment.
Pull request overview
This PR adds support for browsing and rendering the “Elevation of Privilege” (EoP) deck in the SvelteKit website (new edition constant, deck loading, UI toggles, taxonomy/mapping rendering, and i18n strings).
Changes:
- Add
VERSION_EOPand wire EoP (eop,5.0) into deck/suit loading and existing browsing routes. - Extend card-code validation lists and update ordering/color mappings for new EoP suits/cards.
- Introduce EoP-specific UI components (taxonomy + mapping) and extend tests to cover the new deck.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| cornucopia.owasp.org/src/routes/edition/[edition]/+page.svelte | Adds EoP as a selectable edition and renders its description/suits. |
| cornucopia.owasp.org/src/routes/edition/[edition]/[card]/+page.svelte | Extends allowed card IDs to include EoP codes and labels edition name for eop. |
| cornucopia.owasp.org/src/routes/edition/[edition]/[card]/[version]/+page.svelte | Extends allowed card IDs to include EoP codes and labels edition name for eop. |
| cornucopia.owasp.org/src/routes/edition/[edition]/[card]/[version]/[lang]/+page.svelte | Extends allowed card IDs to include EoP codes and labels edition name for eop. |
| cornucopia.owasp.org/src/routes/cards/+page.svelte | Adds EoP suits to the cards browser UI, preview selection, and noscript output. |
| cornucopia.owasp.org/src/routes/cards/+page.server.ts | Loads EoP deck cards server-side and merges into the deck map. |
| cornucopia.owasp.org/src/routes/cards/[card]/+page.svelte | Extends allowed card IDs to include EoP codes and labels edition name for eop. |
| cornucopia.owasp.org/src/routes/cards/[card]/+page.server.ts | Loads EoP deck cards server-side for per-card pages. |
| cornucopia.owasp.org/src/lib/services/decService.integration.test.ts | Updates integration expectations for EoP deck presence and total card counts. |
| cornucopia.owasp.org/src/lib/services/deckServiceConsts.ts | Adds VERSION_EOP. |
| cornucopia.owasp.org/src/lib/services/deckService.ts | Registers EoP deck metadata (latest + available decks). |
| cornucopia.owasp.org/src/lib/services/deckService.test.ts | Adds unit tests for EoP edition/version/language and adjusts deck counts. |
| cornucopia.owasp.org/src/lib/components/eopCardTaxonomy.svelte | New: taxonomy display for EoP cards (mappings + attacks). |
| cornucopia.owasp.org/src/lib/components/eopCardMapping.svelte | New: compact STRIDE mapping renderer for EoP cards in previews. |
| cornucopia.owasp.org/src/lib/components/cardPreview.svelte | Refactors mapping rendering to support EoP mapping component. |
| cornucopia.owasp.org/src/lib/components/cardFound.svelte | Refactors taxonomy rendering to support EoP taxonomy component. |
| cornucopia.owasp.org/src/domain/suit/suitController.ts | Adds EoP deck/language metadata for suit discovery. |
| cornucopia.owasp.org/src/domain/suit/order.ts | Adds suit ordering for STRIDE suit names. |
| cornucopia.owasp.org/src/domain/card/order.ts | Adds per-suit ordering for EoP card IDs. |
| cornucopia.owasp.org/src/domain/card/cardColor.ts | Adds color keys for STRIDE suit names. |
| cornucopia.owasp.org/data/website/phrases/en/cards.json | Adds EoP copy + i18n keys for button/heading and EoP taxonomy headings. |
|
You have the card texts, and the cards are browsable. No styling is applied, but I guess that is what you are working on next? |
|
@rewtd, @sydseter We are close now. That said, there are 4 asset images having a white background instead of transparent (elevation-of-privilege-{2,3,4}.png, tampering-2.png) (See tampering-2 vs tampering 3 below), which hides the watermark. I have several options here. Finding a way to fix those 4 pngs, OR extracting them from the PDF as png, OR handling those cards in css as a workaround. What do you think makes more sense? I won't suggest the last one as we want css to be clean for the later refactoring work. |
@ayman-art First of all, fantastic work. I love it. I believe we could implement a way to handle exceptions, which should be treated as an optional rendering capability. How this should be done is not that important. It could be just a combination of using an image as a background with some css to hold it in place to make it easy to add the card, but it should probably be an option, not the default. |
There was a problem hiding this comment.
Could this be generated by a script?
There was a problem hiding this comment.
Absolutely! I believe a main goal of this project is to provide extensibility and make importing new games into the card browser as easy as possible with minimal additions required. That's why the scaffolding script was probably a good idea. That said, styling was the initial priority for this PR, and yes, some of these additions can be automated as you mentioned. I'll keep this in mind, maybe for a separate PR later, to keep this milestone focused on styling.
There was a problem hiding this comment.
That is fine by me. As I said, it’s up to Grant and you. For now, this is still a great improvement.
There was a problem hiding this comment.
Same here. Could this be generated by a script? Not saying that you need to do this right now. This pr is fine as it is. Just food for thought.
There was a problem hiding this comment.
This as well. Possible to be rendered by a Python script right?
| export class SuitController { | ||
|
|
||
| private static readonly decks = [{edition: 'mobileapp', version: '1.1'}, {edition: 'webapp', version: '3.0'}, {edition: 'companion', version: '1.0'}]; | ||
| private static readonly decks = [{edition: 'mobileapp', version: '1.1'}, {edition: 'webapp', version: '3.0'}, {edition: 'companion', version: '1.0'}, {edition: 'eop', version: '5.0'}]; |
There was a problem hiding this comment.
This, as well, could be moved out and created up front as a configuration that the suiController just consumes.
| } | ||
| </style> | ||
|
|
||
| /* EoP card styles derived from the original PDF design, See |
There was a problem hiding this comment.
This should preferably be externalised and generated as a stylesheet that the cardPreview.svelte uses.
|
|
||
| {#if mappings} | ||
| <h2 id="mapping" class="title">{$t("cards.eopCardTaxonomy.h1.1")}</h2> | ||
| <MappingsList title="STRIDE:" mappings={formatValue(mappings['stride'])} /> |
There was a problem hiding this comment.
This as well. The eopCardTaxonomy.svelte should be generalised so that it can read any type of taxonomy configuration JSON object and create the correct taxonomy based on how it looks. A separate taxonomy attribute in the YAML mapping could be used for this.
| function cardFound() | ||
| function cardFound() | ||
| { | ||
| let cards_options = [ |
| suits : SuitController.getSuits(), | ||
| decks : decks.set(lang, new Map([...mobileCards, ...webappCards, ...companionCards])), | ||
| suits: SuitController.getSuits(), | ||
| decks: decks.set(lang, new Map([...mobileCards, ...webappCards, ...companionCards, ...eopCards])), |
There was a problem hiding this comment.
Instead of ...mobileCards, ...webappCards, ...companionCards, ...eopCards this should simply be: ...decks.
For later. Not now.
| import type { Suit } from "../../domain/suit/suit.js"; | ||
| import { SvelteMap } from 'svelte/reactivity'; | ||
| import { VERSION_WEBAPP, VERSION_MOBILEAPP, VERSION_COMPANION } from "$lib/services/deckServiceConsts"; | ||
| import { VERSION_WEBAPP, VERSION_MOBILEAPP, VERSION_COMPANION, VERSION_EOP } from "$lib/services/deckServiceConsts"; |
There was a problem hiding this comment.
Should be moved out and made configurable later.
| }); | ||
|
|
||
| let eopSuits = $derived.by(() => { | ||
| const langSuits = suits?.get(`${VERSION_EOP}-${$lang}`); |
| {#if map?.get(suit.name)} | ||
| {#each suit.cards as card (card)} | ||
| <p onmouseenter={()=>{enter(suit.name,cards?.get(card)?.id)}}> | ||
| <a title="OWASP Cornucopia {Text.Format(suit.name).toUpperCase()}, {cards?.get(card)?.id}" href={cards?.get(card)?.url ?? ''}>├── {cards?.get(card)?.id}</a> |
There was a problem hiding this comment.
the OWASP Cornucopia prefix to the title might not make sense any more. How does this look like for the EoP cards?
There was a problem hiding this comment.
Perhaps this could be taken from the incoming mapping or card data?
There was a problem hiding this comment.
Right now the tooltip shows "OWASP Cornucopia SPOOFING, SP2" for the SP2 card, so yes, the prefix is misleading here.
I can take it from the card data as you suggested: adding an editionName field to the card model, populated by deckService from a per-edition name map, and using it in the link titles. The title becomes "Elevation of Privilege SPOOFING, SP2" while the other decks keep their current text.
| <div class="card-buttons"> | ||
| {#each suit.cards as card (card)} | ||
| <p> | ||
| <a title="OWASP Cornucopia card: {cards?.get(card)?.id} from suit: {Text.Format(suit.name).toUpperCase()}" href={cards?.get(card)?.url ?? ''}>├── {cards?.get(card)?.id}</a> |
There was a problem hiding this comment.
here as well. The link title needs to come from the card or mapping data.
| function cardFound() | ||
| function cardFound() | ||
| { | ||
| let cards_options = [ |
| return isKnownCardId(card?.id) | ||
| } | ||
|
|
||
| function getEdition(str: string) : string { |
There was a problem hiding this comment.
Ok for now, but this should also come from the card and mapping data.
| function cardFound() | ||
| function cardFound() | ||
| { | ||
| let cards_options = [ |
There was a problem hiding this comment.
Should be added here by the card browser script.
There was a problem hiding this comment.
same here. Later, this should be copied here by the card browser script. All assets could come from a specific folder containing the yaml, images, css and taxonomy needed to create the card browser scaffolding.
| assert Show.latest_version("webapp") == "3.0" | ||
| assert Show.latest_version("ecommerce") == "1.22" | ||
| assert Show.latest_version("eop") == "5.1" | ||
| assert Show.latest_version("eop") == "5.2" |
There was a problem hiding this comment.
I am not sure this test makes sense. I think we should remove the latest_version assertions.
| assert Show.latest_version("cumulus") == "1.1" | ||
| assert Show.latest_version("masvs") == "1.1" | ||
| assert Show.latest_version("eop") == "5.1" | ||
| assert Show.latest_version("eop") == "5.2" |
There was a problem hiding this comment.
remove all versioning assertions.
| assert Show.latest_version("cumulus") == "1.1" | ||
| assert Show.latest_version("masvs") == "1.1" | ||
| assert Show.latest_version("eop") == "5.1" | ||
| assert Show.latest_version("eop") == "5.2" |
There was a problem hiding this comment.
please remove the versioning assertions. It will make it harder to add new editions and doesn't really do anything important.
I won't require you to make any of these changes. It's up to Grant to prioritize.
|
I would look at the title fields for the links before merging. The OWASP Cornucopia prefix will be misleading for the EoP cards in the card browser. |
| "cumulus" -> "1.1" | ||
| "masvs" -> "1.1" | ||
| "eop" -> "5.1" | ||
| "eop" -> "5.2" |
There was a problem hiding this comment.
This should come from a configuration file that is generated up-front
There was a problem hiding this comment.
Could be copied here by a script
There was a problem hiding this comment.
The migration script could probably be generated as well from config.
coming from card data Signed-off-by: Ayman Algamal <ayman01287985950@gmail.com>
Signed-off-by: Ayman Algamal <ayman01287985950@gmail.com>
|
@ayman-art you have a build failure, but the important issues seems to be resolved. |
yes, it seems removing the unnecessary tests of latest versions values has reduced the test coverage to 94.7% |
Signed-off-by: Ayman Algamal <ayman01287985950@gmail.com>
|
@ayman-art You have a conflict you need to resolve. Keep in mind the ordering of the cards has now changed. |
… order Signed-off-by: Ayman Algamal <ayman01287985950@gmail.com>
Resolved. I merged master branch and updated the EoP card order to follow the new Ace-last convention. |






Description
Changes
Feature 1: EoP edition in the Card Browser UI
data/website/phrases/en/cards.json: EoP button, header and descriptionFeature 2: EoP deck and suit registration
Registered eop 5.0/en; suits in source order (SP, TA, RE, ID, DS, EP), starting with Ace like the other editions.
src/lib/services/deckService.ts,deckServiceConsts.ts,src/domain/suit/suitController.tssrc/domain/suit/order.ts,src/domain/card/order.tsFeature 3: Card routes for all 78 EoP cards
SP2 is the default card on version switch (as AAI2 for Companion).
src/domain/card/cardIds.ts: shared card id allowlist used by all cardroutes
src/routes/cards/+page.server.ts,+page.svelte,src/routes/cards/[card]/…src/routes/edition/[edition]/+page.svelte,edition/[edition]/[card]/…and its
[version]/[lang]children (cards_options)Feature 4: EoP card styling faithful to the printed deck
The styles were initially ported from copi, then reworked to match the original deck: element
sizes, positions, fonts and colors are derived from the official EoP card
PDF.
src/lib/components/cardPreview.svelte: reworked EoP styles to match theoriginal design instead of copi:
block); the watermark only renders on non-royal cards
just a loading fallback
and per-card offsets
tab (dark red → gray) and Information Disclosure tab (yellow → blue)
original design
static/images/eop-cards/(78 PNGs): per-card images, the files are taken fromcopi;
Feature 5: STRIDE mappings component
src/lib/components/eopCardMapping.svelte: renders the stride fieldcardPreview.svelte: mappings refactored via a component map so that future editions will require changing one line.Feature 6: Card taxonomy
Threat Modeling template from M1, Mappings and Attacks sections, same structure as Companion.
src/lib/components/eopCardTaxonomy.svelte,cardFound.svelte(same component-map refactor)data/website/phrases/en/cards.json: two translation keysFeature 7: Copi cards link to cornucopia card pages
EoP cards in copi games now show a "Need more info?" link that opens the card's
page on cornucopia.owasp.org to help players know more about the card.
priv/repo/cornucopia/eop-cards-5.2-en.yaml: new deck version as a copy of the5.1 deck with
url: "https://cornucopia.owasp.org/cards/<id>"added to all 78cards
priv/repo/migrations/20260717100557_populate_eop_cards52.exs: imports the5.2 deck via the existing
add_cards_to_database/2lib/copi_web/live/game_live/show.ex:latest_version("eop")increased to"5.2" so game sessions use the new deck (tests updated accordingly)
lib/copi_web/controllers/card_html/show.html.heex: shows the link on EoPcards that have a url, same as companion
Feature 8: Tests
deckService.test.ts,decService.integration.test.ts: EoP registration coveredScreenshots:
Notes
elevation-of-privilege-{2,3,4}.png,tampering-2.png)had an opaque white background that hid the watermark
Resolved or fixed issue: none (partial progress on #1322)
AI Tool Disclosure
[e.g. GitHub CoPilot, ChatGPT, JetBrains Junie etc.][e.g. GPT-4.1, Claude Haiku 4.5, Gemini 2.5 Pro etc.][Summarize the key prompts or instructions given to the AI tools]Affirmation