Skip to content
Closed
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1805397
Adding more tests and refactoring code in order to improve the overal…
sydseter Jan 20, 2026
39fcbb2
Get rid of warnings
sydseter Jan 20, 2026
ca273ec
Remove script
sydseter Jan 20, 2026
9793bbf
Fix linux build issue
sydseter Jan 20, 2026
bf077c3
Try to resolve path depending on os
sydseter Jan 20, 2026
2e4d7e1
Adding capec scripts
sydseter Jan 21, 2026
7409bb2
Fix convert test
sydseter Jan 21, 2026
509c618
Fix style issues
sydseter Jan 21, 2026
f5e6b74
Fix test and formating
sydseter Jan 21, 2026
2aa8400
Fix code style issues
sydseter Jan 22, 2026
062b036
Fix build issues
sydseter Jan 22, 2026
69e3d65
Resolve merge issues
sydseter Jan 22, 2026
6fa8fa4
Resolve merge issues
sydseter Jan 22, 2026
46c04b6
Update lock file
sydseter Jan 22, 2026
e814e1f
Fix coding style issues
sydseter Jan 22, 2026
d768c12
Fix possible type juggling issues
sydseter Jan 22, 2026
60f003a
Fix style isues for dockerfile
sydseter Jan 22, 2026
286fcbb
Fix more coding style issues
sydseter Jan 22, 2026
712c252
Fix coding issues
sydseter Jan 22, 2026
ebd678b
Fix coding style issues
sydseter Jan 22, 2026
94adcac
Try to fix clusterfuck
sydseter Jan 22, 2026
0b9c86a
Fix test issues after code style improvements
sydseter Jan 22, 2026
5a2d087
Fix formating
sydseter Jan 22, 2026
6952670
Fix coding style issues
sydseter Jan 22, 2026
58a2825
Try different package
sydseter Jan 22, 2026
3e1085f
Update deps
sydseter Jan 22, 2026
6ca87b6
Install the clusterfuck dependencies in two runs
sydseter Jan 22, 2026
c9a901e
Abort clusterfuck linting fixes
sydseter Jan 22, 2026
dd74ecd
Ensure build breaks if the coverage is too low
sydseter Jan 22, 2026
020188f
Add vitest.config
sydseter Jan 22, 2026
f5ada75
Correctly update deps
sydseter Jan 22, 2026
ce8af4d
Try to go back to the previous know good state
sydseter Jan 22, 2026
ad0fd36
Fix the fuzzer
sydseter Jan 22, 2026
4817ee1
Upgrade to python 3.12 and fix the fuzzers
sydseter Jan 22, 2026
bf645e6
Ensure pipenv is installe
sydseter Jan 22, 2026
9cf2794
Ensure lf is correct
sydseter Jan 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 126 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,97 @@ options:
You can also speficy your own layout. If so, there needs to be a yaml file in the source folder where the name contains the layout code. Eg. edition-layout-ver-lang.yaml
```

## Additional Utility Scripts

### Converting CAPEC Data

The `scripts/convertCAPEC.py` script converts CAPEC (Common Attack Pattern Enumeration and Classification) JSON data into Markdown format for the Cornucopia website taxonomy.

```bash
python ./scripts/convertCAPEC.py --help
usage: convertCAPEC.py [-h] [-o OUTPUT_PATH] [-i INPUT_PATH] [-d]

Convert CAPEC JSON to Cornucopia format

options:
-h, --help show this help message and exit
-o OUTPUT_PATH, --output-path OUTPUT_PATH
Path to store converted CAPEC files
-i INPUT_PATH, --input-path INPUT_PATH
Path to read CAPEC JSON files from
-d, --debug Output additional information to debug script
```

**Example usage:**

```bash
Comment thread
sydseter marked this conversation as resolved.
# Convert CAPEC data using default paths
python scripts/convertCAPEC.py

# Convert with custom input and output paths
python scripts/convertCAPEC.py -i data/capec-3.9/3000.json -o cornucopia.owasp.org/data/taxonomy/en/CAPEC-3.9

# Enable debug logging
python scripts/convertCAPEC.py -d
```

**Default paths:**

- Input: `cornucopia.owasp.org/data/capec-3.9/3000.json`
Comment thread
sydseter marked this conversation as resolved.
Comment thread
sydseter marked this conversation as resolved.
- Output: `cornucopia.owasp.org/data/taxonomy/en/CAPEC-3.9/`
Comment thread
sydseter marked this conversation as resolved.
Comment thread
sydseter marked this conversation as resolved.

The script creates individual Markdown files for each CAPEC attack pattern with descriptions and links to the official CAPEC database.

### Converting CAPEC Mappings to ASVS Format

The `scripts/convertCAPECMapToASVSMap.py` script processes webapp-mappings YAML files and generates a consolidated CAPEC-to-ASVS (Application Security Verification Standard) mapping file.

```bash
python ./scripts/convertCAPECMapToASVSMap.py --help
usage: convertCAPECMapToASVSMap.py [-h] [-i INPUT_PATH] [-o OUTPUT_PATH] [-d]

Convert webapp-mappings YAML to CAPEC-to-ASVS mapping format

options:
-h, --help show this help message and exit
-i INPUT_PATH, --input-path INPUT_PATH
Path to input webapp-mappings YAML file
-o OUTPUT_PATH, --output-path OUTPUT_PATH
Path to save converted CAPEC-to-ASVS mapping YAML file
-d, --debug Output additional information to debug script
```

**Example usage:**

```bash
Comment thread
sydseter marked this conversation as resolved.
# Convert mappings using default paths
python scripts/convertCAPECMapToASVSMap.py

# Convert with custom input and output paths
python scripts/convertCAPECMapToASVSMap.py -i source/webapp-mappings-3.0.yaml -o source/webapp-capec-3.0.yaml

# Enable debug logging
python scripts/convertCAPECMapToASVSMap.py -d
```

**Default paths:**

- Input: `source/webapp-mappings-3.0.yaml`
Comment thread
sydseter marked this conversation as resolved.
Comment thread
sydseter marked this conversation as resolved.
- Output: `source/webapp-capec-3.0.yaml`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unordered list style [markdownlint:MD004]

Comment thread
qltysh[bot] marked this conversation as resolved.

The script:
1. Reads CAPEC mappings from the `suits -> cards -> capec_map` structure

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lists should be surrounded by blank lines [markdownlint:MD032]

Comment thread
qltysh[bot] marked this conversation as resolved.
2. Merges all OWASP ASVS requirements for each unique CAPEC code
3. Outputs a unified YAML file mapping CAPEC codes to their associated ASVS requirements

**Output format:**
```yaml

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fenced code blocks should be surrounded by blank lines [markdownlint:MD031]

Comment thread
qltysh[bot] marked this conversation as resolved.
54:
owasp_asvs: [4.3.2, 13.2.2, 13.4.1, ...]
116:
owasp_asvs: [13.2.2, 15.2.3, ...]
```

## Printing

The latest printable files are released under the [pre-release](https://github.com/OWASP/cornucopia/releases/tag/pre-release). Please download final printable files from there.
Expand Down Expand Up @@ -175,7 +266,7 @@ On Mac OSX and Ubuntu you may not need to go through all of these steps, but thi
# python -c "import os, sys; print(os.path.dirname(sys.executable))"
python -m pipenv shell --python "{path to python}"
pip install pipenv
pipenv install
pipenv install --dev


### Coding style
Expand All @@ -199,6 +290,40 @@ Run Coding Style Check
pipenv run flake8 --max-line-length=120 --max-complexity=10 --ignore=E203,W503 --exclude ./.venv/
pipenv run mypy --namespace-packages --strict ./scripts/

### Docker container

You can also run the converter inside a docker container. To build the container run:

```powershell
docker build --target pipenv `
--build-arg user_id=1000 `
--build-arg group_id=1000 `
--build-arg home=/home/builder `
--build-arg workdir=/workspace `
-t cornucopia-converter .
```

To login to the container and mount the current working directory inside the container run:

```powershell
docker run --rm -it --entrypoint "/bin/bash" -v ${PWD}:/workspace cornucopia-converter
```

Run the converter tests inside the container:

```powershell

#unit tests
docker run --rm -v ${PWD}:/workspace cornucopia-converter run coverage run --append --branch --omit "*_?test.py,*/.local/*" --module unittest discover --verbose --start-directory "tests/scripts" --pattern "*_utest.py"

#integration tests
docker run --rm -v ${PWD}:/workspace cornucopia-converter run coverage run --append --branch --omit "*_?test.py,*/.local/*" --module unittest discover --verbose --start-directory "tests/scripts" --pattern "*_utest.py"

#See code coverage
docker run --rm -v ${PWD}:/workspace cornucopia-converter run coverage report scripts/convert.py

```

### Static analysis

run static analysis checks
Expand Down
5 changes: 5 additions & 0 deletions cornucopia.owasp.org/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ vite.config.ts.timestamp-*
/.vs
package-lock.json
coverage/**

# SvelteKit files
/.svelte
**/.svelte-kit
**/coverage
6 changes: 6 additions & 0 deletions cornucopia.owasp.org/src/domain/deck/deck.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type Deck =
{
edition : string,
version : string,
lang : string[]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import {expect, describe, it} from 'vitest';
import { MappingController } from './mappingController';


describe('MappingController tests', () => {
it("should return web app card mapping data.", async () => {

const mappingData = {
suits: [
{
name: "Test Suit",
cards: [
{
id: "webapp-1",
owasp_asvs: ["1.1", "1.2"],
name: "Test Card"
}
]
}
]
};
const controller = new MappingController(mappingData);

const webAppMapping = controller.getWebAppCardMappings("webapp-1");
expect(webAppMapping).toBeDefined();
expect(webAppMapping.id).toBe("webapp-1");
expect(webAppMapping.owasp_asvs).toEqual(["1.1", "1.2"]);
});

it("should return mobile app card mapping data.", async () => {

const mappingData = {
suits: [
{
name: "Test Suit",
cards: [
{
id: "mobileapp-1",
owasp_masvs: ["MASVS-1", "MASVS-2"],
owasp_mastg: ["MASTG-1"],
capec: [1, 2],
safecode: [101, 102]
}
]
}
]
};
const controller = new MappingController(mappingData);
const mobileAppMapping = controller.getMobileAppCardMappings("mobileapp-1");
expect(mobileAppMapping).toBeDefined();
expect(mobileAppMapping.id).toBe("mobileapp-1");
expect(mobileAppMapping.owasp_masvs).toEqual(["MASVS-1", "MASVS-2"]);
expect(mobileAppMapping.capec).toEqual([1, 2]);
expect(mobileAppMapping.safecode).toEqual([101, 102]);

});

it("should return empty mapping for non-existing card.", async () => {
const mappingData = {
suits: []
};
const controller = new MappingController(mappingData);
const webAppMapping = controller.getWebAppCardMappings("non-existing-card");
expect(webAppMapping).toBeDefined();
expect(Object.keys(webAppMapping).length).toBe(0);

const controller2 = new MappingController({});
const webAppMapping2 = controller2.getWebAppCardMappings("non-existing-card");
expect(webAppMapping2).toBeDefined();
expect(Object.keys(webAppMapping2).length).toBe(0);
});

it("should return meta information.", async () => {
const mappingData = {
meta: { version: "1.0", date: "2024-01-01" },
suits: []
};
const controller = new MappingController(mappingData);
const meta = controller.getMeta();
expect(meta).toBeDefined();
expect(meta.version).toBe("1.0");
expect(meta.date).toBe("2024-01-01");
});
});
14 changes: 7 additions & 7 deletions cornucopia.owasp.org/src/domain/mapping/mappingController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ export type WebAppMapping =
owasp_appsensor : string[],
capec : number[],
safecode : number[],
capec_map : {
[key: number]: {
owasp_asvs: (string)[],
name: string,
id: number
}
};

}

Expand All @@ -34,13 +41,6 @@ export class MappingController {
this.mapping = mapping;
}

private static parseSuit(suit : string) : string
{
suit = suit.replaceAll("-" , " ");
return suit;

}

public getWebAppCardMappings(card : string) : WebAppMapping
{
return this.getCardMappings(card) as WebAppMapping;
Expand Down
14 changes: 14 additions & 0 deletions cornucopia.owasp.org/src/domain/mapping/mastg.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {expect, describe, it} from 'vitest';
import { MASTG_TESTS_MAPPING } from './mastg';

describe('MASTG_TESTS_MAPPING tests', () => {
it("should have correct mapping for TEST-0001 to TEST-0059.", async () => {
expect(MASTG_TESTS_MAPPING["TEST-0001"]).toBe("STORAGE");
expect(MASTG_TESTS_MAPPING["TEST-0025"]).toBe("CODE");

expect(MASTG_TESTS_MAPPING["TEST-0038"]).toBe("RESILIENCE");
expect(MASTG_TESTS_MAPPING["TEST-0059"]).toBe("PLATFORM");
expect(MASTG_TESTS_MAPPING["TEST-00600"]).toBeUndefined();
expect(Object.keys(MASTG_TESTS_MAPPING).length).toBe(93);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

let { blogpost }: Props = $props();

let authorLink : string = '/author/' + blogpost.author;
let authorLink = $derived('/author/' + blogpost.author);
</script>

<div class="metadata">
Expand Down
4 changes: 2 additions & 2 deletions cornucopia.owasp.org/src/lib/components/cardBrowser.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

let { card = $bindable(), cards, mappingData }: Props = $props();
let t = readTranslation();
let nextCard = cards.get(card.next);
let previousCard = cards.get(card.prevous);
let nextCard = $derived(cards.get(card.next));
let previousCard = $derived(cards.get(card.prevous));
function checkKey(event : any)
{
const KEYCODE_RIGHT = 39;
Expand Down
2 changes: 1 addition & 1 deletion cornucopia.owasp.org/src/lib/components/cardFound.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
language
}: Props = $props();

const controller: MappingController = new MappingController(mappingData);
const controller = $derived(new MappingController(mappingData));
let t = readTranslation();
let mappings = $state(controller.getCardMappings(card.id));
let attacks: Attack[] = $state(GetCardAttacks(card.id));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,7 @@
}

let { card = $bindable(), mapping, style = '' }: Props = $props();
let previewStyle = $state('');

if (style) {
previewStyle = ' ' + style;
}
let previewStyle = $derived(style ? ' ' + style : '');

function getSuitColor(suit : string, id: string)
{
Expand Down
2 changes: 1 addition & 1 deletion cornucopia.owasp.org/src/lib/components/deck/deck.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

// Manual selection of cards to display on the frontpage
let selectedCards : string[] = ["JOA","C7","CR6","AZ5","SM4","VE3","AT2",]
let mappingData : any[] = mapping.suits.map((suit: { cards: any[]; }) => suit.cards[0]).reverse();
let mappingData : any[] = $derived(mapping.suits.map((suit: { cards: any[]; }) => suit.cards[0]).reverse());

mappingData.unshift({
id: "JOA",
Expand Down
5 changes: 2 additions & 3 deletions cornucopia.owasp.org/src/lib/components/footer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
}

let { timestamp, content }: Props = $props();
let source = content;
let timeAgo : string = $state('');

function doOnMount()
Expand Down Expand Up @@ -51,8 +50,8 @@
<div class="flex-container">
<div class="box">
<p class="title">OWASP Cornucopia</p>
{#if source != ''}
<SvelteMarkdown {renderers} {source}></SvelteMarkdown>
{#if content != ''}
<SvelteMarkdown {renderers} source={content}></SvelteMarkdown>
{/if}
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@

let { mapping, style }: Props = $props();

let mappingStyle = $state('');

if (style) {
mappingStyle = ' ' + style;
}
let mappingStyle = $derived(style ? ' ' + style : '');

</script>
<p class="mapping-title{mappingStyle}">OWASP MASVS</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

let { mappingData, card = $bindable(), routes }: Props = $props();

const controller: MappingController = new MappingController(mappingData);
const controller = $derived(new MappingController(mappingData));
let t = readTranslation();
function linkMASVS(requirement: string) {
let parts = String(requirement).split("-");
Expand Down
Loading
Loading