diff --git a/cornucopia.owasp.org/src/domain/cre/creController.ts b/cornucopia.owasp.org/src/domain/cre/creController.ts index bd9d218b0..c0f51b4e6 100644 --- a/cornucopia.owasp.org/src/domain/cre/creController.ts +++ b/cornucopia.owasp.org/src/domain/cre/creController.ts @@ -36,14 +36,16 @@ export class CreController { ['webapp', "OWASP Cornucopia Website App Edition"], ['mobileapp', "OWASP Cornucopia Mobile App Edition"], ['companion', "OWASP Cornucopia Companion Edition"], - ['dbd', "Cornucopia Digital Benefits and Disbenefits Edition"] + ['dbd', "Cornucopia Digital Benefits and Disbenefits Edition"], + ['eop', "Elevation of Privilege Edition"] ]); private static category: Map = new Map([ ['webapp', "Website Application"], ['mobileapp', "Mobile Application"], ['companion', "Companion suits"], - ['dbd', "Digital Benefits and Disbenefits"] + ['dbd', "Digital Benefits and Disbenefits"], + ['eop', "Elevation of Privilege"] ]); constructor(deck: Map, controller: MappingController) { diff --git a/cornucopia.owasp.org/src/domain/suit/suitController.ts b/cornucopia.owasp.org/src/domain/suit/suitController.ts index c8335b21b..c428770b7 100644 --- a/cornucopia.owasp.org/src/domain/suit/suitController.ts +++ b/cornucopia.owasp.org/src/domain/suit/suitController.ts @@ -10,7 +10,7 @@ export class SuitController { ['mobileapp', {lang: ['en']}], ['webapp', {lang: ['en', 'es', 'fr', 'nl', 'no_nb', 'pt_br', 'pt_pt', 'ru', 'it']}], ['companion', {lang: ['en']}], - ['eop', {lang: ['en']}] + ['eop', {lang: ['en', 'es', 'ru']}] ]); public static getSuits() : Map diff --git a/cornucopia.owasp.org/src/lib/services/deckService.test.ts b/cornucopia.owasp.org/src/lib/services/deckService.test.ts index 9c8c6c2ed..43b39731f 100644 --- a/cornucopia.owasp.org/src/lib/services/deckService.test.ts +++ b/cornucopia.owasp.org/src/lib/services/deckService.test.ts @@ -144,7 +144,7 @@ describe('DeckService tests', () => { expect(decks).toHaveLength(6); expect(decks).toContainEqual({ edition: 'mobileapp', version: '1.1', lang: ['en', 'hi', 'uk'] }); expect(decks).toContainEqual({ edition: 'companion', version: '1.0', lang: ['en'] }); - expect(decks).toContainEqual({ edition: 'eop', version: '5.0', lang: ['en'] }); + expect(decks).toContainEqual({ edition: 'eop', version: '5.0', lang: ['en', 'es', 'ru'] }); expect(decks).toContainEqual({ edition: 'dbd', version: '1.0', lang: ['en'] }); expect(decks).toContainEqual({ edition: 'webapp', @@ -248,9 +248,9 @@ describe('DeckService tests', () => { expect(languages).toEqual(['en']); }); - it('should return only en for eop version 5.0', () => { + it('should return all supported languages for eop version 5.0', () => { const languages = DeckService.getLanguagesForEditionVersion('eop', '5.0'); - expect(languages).toEqual(['en']); + expect(languages).toEqual(['en', 'es', 'ru']); }); it('should return empty array for unknown version', () => { diff --git a/cornucopia.owasp.org/src/lib/services/deckService.ts b/cornucopia.owasp.org/src/lib/services/deckService.ts index 77f4d28a9..8fdb2ec8e 100644 --- a/cornucopia.owasp.org/src/lib/services/deckService.ts +++ b/cornucopia.owasp.org/src/lib/services/deckService.ts @@ -18,14 +18,14 @@ export class DeckService { { lang: ['en', 'hi', 'uk'], edition: 'mobileapp', version: '1.1' }, { lang: ['en', 'es', 'fr', 'nl', 'no_nb', 'pt_br', 'pt_pt', 'ru', 'it', 'hi', 'uk'], edition: 'webapp', version: '3.0' }, { lang: ['en'], edition: 'companion', version: '1.0' }, - { lang: ['en'], edition: 'eop', version: '5.0' } + { lang: ['en', 'es', 'ru'], edition: 'eop', version: '5.0' } ]; private static readonly decks: Deck[] = [ { edition: 'mobileapp', version: '1.1', lang: ['en', 'hi', 'uk'] }, { edition: 'webapp', version: '3.0', lang: ['en', 'es', 'fr', 'nl', 'no_nb', 'pt_br', 'pt_pt', 'ru', 'it', 'hi', 'uk'] }, { edition: 'webapp', version: '2.2', lang: ['en', 'es', 'fr', 'nl', 'no_nb', 'pt_br', 'pt_pt', 'ru', 'it'] }, { edition: 'companion', version: '1.0', lang: ['en'] }, - { edition: 'eop', version: '5.0', lang: ['en'] }, + { edition: 'eop', version: '5.0', lang: ['en', 'es', 'ru'] }, { edition: 'dbd', version: '1.0', lang: ['en'] } ]; diff --git a/cornucopia.owasp.org/src/routes/api/cre/[edition]/[lang]/+server.ts b/cornucopia.owasp.org/src/routes/api/cre/[edition]/[lang]/+server.ts index 613b62ae3..f0a242aee 100644 --- a/cornucopia.owasp.org/src/routes/api/cre/[edition]/[lang]/+server.ts +++ b/cornucopia.owasp.org/src/routes/api/cre/[edition]/[lang]/+server.ts @@ -11,7 +11,7 @@ const _responseInit: ResponseInit = { }, }; -const editions = ["webapp", "mobileapp", "companion", "dbd"] +const editions = ["webapp", "mobileapp", "companion", "dbd", "eop"] export const GET: RequestHandler = ({ url }) => { const params = url.pathname.split('/'); diff --git a/cornucopia.owasp.org/src/routes/api/cre/[edition]/[lang]/server.test.ts b/cornucopia.owasp.org/src/routes/api/cre/[edition]/[lang]/server.test.ts index 46033bd72..49460b62d 100644 --- a/cornucopia.owasp.org/src/routes/api/cre/[edition]/[lang]/server.test.ts +++ b/cornucopia.owasp.org/src/routes/api/cre/[edition]/[lang]/server.test.ts @@ -119,6 +119,38 @@ describe('GET /api/cre/[edition]/[lang]', () => { + it('returns valid CRE mapping response for eop', () => { + vi.spyOn(DeckService, 'getLatestVersion').mockReturnValue('5.0'); + + vi.spyOn(DeckService.prototype, 'getCardDataForEditionVersionLang') + .mockReturnValue(new Map([ + ['SP2', { + id: 'SP2', + edition: 'eop', + url: '/cards/SP2', + suitNameLocal: 'Spoofing', + desc: 'Spoofing threat' + }] + ]) as any); + + vi.spyOn(MappingService.prototype, 'getCardMappingForLatestEdtions') + .mockReturnValue(new Map([ + ['eop', { + meta: { version: '5.0' }, + suits: [{ + cards: [{ + id: 'SP2', + stride: ['S'] + }] + }] + }] + ]) as any); + + const response = GET({ url: new URL('http://localhost/api/cre/eop/en') } as any); + + expect(response).toBeDefined(); + }); + it('falls back to webapp and en defaults when url has no path segments', () => { vi.spyOn(DeckService, 'getLanguages').mockReturnValue(['en']); vi.spyOn(DeckService, 'getLatestVersion').mockReturnValue('3.0'); diff --git a/cornucopia.owasp.org/src/routes/api/lang/[edition]/[version]/server.test.ts b/cornucopia.owasp.org/src/routes/api/lang/[edition]/[version]/server.test.ts index 810b71806..3748a5808 100644 --- a/cornucopia.owasp.org/src/routes/api/lang/[edition]/[version]/server.test.ts +++ b/cornucopia.owasp.org/src/routes/api/lang/[edition]/[version]/server.test.ts @@ -34,6 +34,29 @@ describe('GET /api/lang/[edition]/[version]', () => { }); }); + it('returns languages for the eop edition and version', async () => { + vi.spyOn(DeckService, 'hasEdition').mockReturnValue(true); + vi.spyOn(DeckService, 'hasVersion').mockReturnValue(true); + vi.spyOn(DeckService, 'getLanguagesForEditionVersion').mockReturnValue(['en', 'es', 'ru']); + + const response = await GET({ + params: { edition: 'eop', version: '5.0' } + } as unknown); + + expect(response.status).toBe(200); + expect(response.headers.get('content-type')).toContain('application/json'); + + const body = await response.json(); + expect(body).toEqual({ + meta: { + component: 'cards', + language: 'all', + languages: ['en', 'es', 'ru'], + version: '5.0' + } + }); + }); + it('throws 404 when edition is invalid', () => { vi.spyOn(DeckService, 'hasEdition').mockReturnValue(false); vi.spyOn(DeckService, 'getLatestEditions').mockReturnValue(['webapp', 'mobileapp']); diff --git a/cornucopia.owasp.org/static/api/openapi.yaml b/cornucopia.owasp.org/static/api/openapi.yaml index d504907af..74ecc9c91 100644 --- a/cornucopia.owasp.org/static/api/openapi.yaml +++ b/cornucopia.owasp.org/static/api/openapi.yaml @@ -4,8 +4,8 @@ info: title: OWASP Cornucopia API description: | Public API for accessing OWASP Cornucopia card data and metadata. - This API exposes language metadata and Open CRE mappings - for both Website App and Mobile App editions. + This API exposes language metadata, card mappings and Open CRE mappings + for the Cornucopia editions and the Elevation of Privilege edition. version: 1.0.0 servers: @@ -106,6 +106,36 @@ paths: languages: [en] version: "1.0" + /lang/eop/{version}: + get: + summary: Get supported languages for Elevation of Privilege edition + description: | + Returns metadata about supported languages for the + Elevation of Privilege Edition. + parameters: + - name: version + in: path + required: true + schema: + type: string + enum: ["5.0"] + responses: + '200': + description: Language metadata for Elevation of Privilege edition + content: + application/json: + schema: + $ref: '#/components/schemas/LanguageMetaEop' + examples: + example: + summary: Example language metadata response + value: + meta: + component: cards + language: all + languages: [en, es, ru] + version: "5.0" + /cre/dbd/{lang}: get: summary: Get DBD cards by language @@ -368,6 +398,43 @@ paths: capec: [ 508 ] safecode: [ "-" ] + /mapping/eop/{version}: + get: + summary: Get Elevation of Privilege mapping by version + description: | + Get the Elevation of Privilege Edition mapping data + by version. Each card just maps to its STRIDE category. + parameters: + - name: version + in: path + required: true + schema: + type: string + enum: ["5.0"] + responses: + '200': + description: Elevation of Privilege mapping data + content: + application/json: + schema: + type: object + examples: + example: + summary: Example Elevation of Privilege mapping response + value: + meta: + edition: eop + component: mappings + language: ALL + version: "5.0" + cards: + SP2: + id: "SP2" + value: "2" + url: "https://cornucopia.owasp.org/cards/SP2" + stride: [ S ] + stride_print: [ Spoofing ] + /cre/mobileapp/{lang}: get: summary: Get Mobile cards by language @@ -460,19 +527,61 @@ paths: version: "1.0" standards: [] + /cre/eop/{lang}: + get: + summary: Get Elevation of Privilege cards by language + description: | + Get the Elevation of Privilege Edition cards by language. + This edition does not have Open CRE mappings, so the links + array of each card is empty. + parameters: + - name: lang + in: path + required: true + schema: + type: string + enum: [en, es, ru] + responses: + '200': + description: Elevation of Privilege cards + content: + application/json: + schema: + type: object + examples: + example: + summary: Example Elevation of Privilege response + value: + meta: + edition: Elevation of Privilege Edition + component: cards + language: en + version: "5.0" + standards: + - doctype: Tool + id: https://cornucopia.owasp.org/edition/eop/SP2/5.0/en + name: Elevation of Privilege Edition + section: Spoofing + description: An attacker could take over the port or socket that the server normally uses + sectionID: SP2 + hyperlink: https://cornucopia.owasp.org/edition/eop/SP2/5.0/en + links: [] + tags: [Threat modeling, Elevation of Privilege] + tooltype: Defensive + /lang/{edition}/{version}: get: summary: Get language metadata for an edition version description: | Returns the list of supported languages for a given edition and version. - Works for all registered editions (webapp, mobileapp, companion). + Works for all registered editions (webapp, mobileapp, companion, eop). parameters: - name: edition in: path required: true schema: type: string - enum: [webapp, mobileapp, companion] + enum: [webapp, mobileapp, companion, eop] - name: version in: path required: true @@ -595,26 +704,47 @@ components: version: type: string example: "1.0" -LanguageMetaCompanion: + LanguageMetaCompanion: + type: object + properties: + meta: type: object properties: - meta: - type: object - properties: - edition: - type: string - example: OWASP Cornucopia Companion Edition - component: - type: string - example: cards - language: - type: string - example: all - languages: - type: array - items: - type: string - example: [en] - version: - type: string - example: "1.0" + edition: + type: string + example: OWASP Cornucopia Companion Edition + component: + type: string + example: cards + language: + type: string + example: all + languages: + type: array + items: + type: string + example: [en] + version: + type: string + example: "1.0" + + LanguageMetaEop: + type: object + properties: + meta: + type: object + properties: + component: + type: string + example: cards + language: + type: string + example: all + languages: + type: array + items: + type: string + example: [en, es, ru] + version: + type: string + example: "5.0"