Skip to content

Commit b83a2f1

Browse files
Merge pull request #230 from jkowalleck/v1.5-schemaParser-AJV
fix JSON schema issues found by AJV
2 parents 5b65a9e + d7f689e commit b83a2f1

File tree

7 files changed

+198
-85
lines changed

7 files changed

+198
-85
lines changed

.github/workflows/js.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# docs: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions
2+
3+
name: JS CI
4+
5+
on: [push, pull_request, workflow_dispatch]
6+
7+
concurrency:
8+
group: ${{ github.workflow }}-${{ github.ref }}
9+
cancel-in-progress: true
10+
11+
12+
defaults:
13+
run:
14+
working-directory: tools/src/test/js
15+
16+
jobs:
17+
test:
18+
timeout-minutes: 30
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout
22+
# see https://github.com/actions/checkout
23+
uses: actions/checkout@v3
24+
- name: Setup Node.js
25+
# see https://github.com/actions/setup-node
26+
uses: actions/setup-node@v3
27+
with:
28+
node-version: '20.x'
29+
- name: Install Depenencies
30+
run: npm install
31+
- name: Run test
32+
run: npm test

schema/bom-1.5.schema.json

Lines changed: 9 additions & 81 deletions
Large diffs are not rendered by default.

schema/jsf-0.82.schema.json

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
"type": "array",
1717
"title": "Signature",
1818
"description": "Unique top level property for Multiple Signatures. (multisignature)",
19-
"additionalItems": false,
2019
"items": {"$ref": "#/definitions/signer"}
2120
}
2221
}
@@ -28,7 +27,6 @@
2827
"type": "array",
2928
"title": "Signature",
3029
"description": "Unique top level property for Signature Chains. (signaturechain)",
31-
"additionalItems": false,
3230
"items": {"$ref": "#/definitions/signer"}
3331
}
3432
}
@@ -94,7 +92,6 @@
9492
"type": "array",
9593
"title": "Certificate path",
9694
"description": "Optional. Sorted array of X.509 [RFC5280] certificates, where the first element must contain the signature certificate. The certificate path must be contiguous but is not required to be complete.",
97-
"additionalItems": false,
9895
"items": {
9996
"type": "string"
10097
}
@@ -103,7 +100,6 @@
103100
"type": "array",
104101
"title": "Excludes",
105102
"description": "Optional. Array holding the names of one or more application level properties that must be excluded from the signature process. Note that the \"excludes\" property itself, must also be excluded from the signature process. Since both the \"excludes\" property and the associated data it points to are unsigned, a conforming JSF implementation must provide options for specifying which properties to accept.",
106-
"additionalItems": false,
107103
"items": {
108104
"type": "string"
109105
}

tools/src/test/js/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/node_modules/
2+
/package-lock.json

tools/src/test/js/README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# validate the JSON schema with `AJV`
2+
3+
uses https://ajv.js.org/
4+
for validation of a schema.
5+
6+
## requirements
7+
8+
* node >=16
9+
10+
## setup
11+
12+
```shell
13+
npm install
14+
```
15+
16+
## usage
17+
18+
```shell
19+
npm test
20+
```
21+
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
"use strict";
2+
3+
import assert from 'assert'
4+
import {readFile} from 'fs/promises'
5+
import {dirname, basename, join} from 'path'
6+
import {fileURLToPath} from 'url'
7+
8+
import Ajv from 'ajv'
9+
import addFormats from 'ajv-formats'
10+
import addFormats2019 from 'ajv-formats-draft2019'
11+
import {glob} from 'glob'
12+
13+
// region config
14+
15+
const bomSchemasGlob = 'bom-*.schema.json'
16+
const schemaDir = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..', '..', 'schema')
17+
18+
// endregion config
19+
20+
const [spdxSchema, jsfSchema, bomSchemas] = await Promise.all([
21+
readFile(join(schemaDir, 'spdx.schema.json'), 'utf-8').then(JSON.parse),
22+
readFile(join(schemaDir, 'jsf-0.82.schema.json'), 'utf-8').then(JSON.parse),
23+
glob(join(schemaDir, bomSchemasGlob)).then(l => l.sort())
24+
])
25+
assert.notStrictEqual(bomSchemas.length, 0)
26+
27+
/**
28+
* @param {boolean|"log"} strict
29+
* @return {Ajv}
30+
*/
31+
function getAjv(strict) {
32+
// see https://ajv.js.org/options.html
33+
const ajv = new Ajv({
34+
strict: strict,
35+
strictSchema: strict,
36+
strictNumbers: strict,
37+
strictTypes: strict,
38+
strictTuples: strict,
39+
/* This parser has issues with the oneOf-required in
40+
* `{ type: 'object', oneOf:[{required:['a']},{required:['b']}], properties:{a:{...},b:{...}} }`
41+
* So `strictRequired` must be `false` tor our schema files.
42+
*
43+
* This is a known and expected behaviour.
44+
* see https://ajv.js.org/strict-mode.html#defined-required-properties
45+
* > Property defined in parent schema
46+
* > There are cases when property defined in the parent schema will not be taken into account.
47+
*/
48+
strictRequired: false,
49+
validateFormats: true,
50+
allowMatchingProperties: true,
51+
addUsedSchema: false,
52+
schemas: {
53+
'http://cyclonedx.org/schema/spdx.schema.json': spdxSchema,
54+
'http://cyclonedx.org/schema/jsf-0.82.schema.json': jsfSchema
55+
}
56+
});
57+
addFormats(ajv)
58+
addFormats2019(ajv, {formats: ['idn-email']})
59+
// there is just no working implementation for format "iri-reference"
60+
// see https://github.com/luzlab/ajv-formats-draft2019/issues/22
61+
ajv.addFormat('iri-reference', true)
62+
return ajv
63+
}
64+
65+
let errCnt = 0
66+
67+
for (const bomSchemaFile of bomSchemas) {
68+
console.log('\n> SchemaFile: ', bomSchemaFile);
69+
const v = /^bom-(\d)\.(\d)/.exec(basename(bomSchemaFile)) ?? []
70+
if (!v[0]) {
71+
// test match failed
72+
console.log('> Skipped.')
73+
continue
74+
}
75+
76+
const cdxVersion = [Number(v[1]), Number(v[2])]
77+
const strict = cdxVersion >= [1, 5]
78+
? true
79+
: 'log'
80+
console.debug('> strict: ', strict)
81+
const ajv = getAjv(strict)
82+
83+
if (cdxVersion[0] === 1 && cdxVersion[1] === 2) {
84+
// CycloneDX 1.2 had a wrong undefined format `string`.
85+
// Let's ignore this format only for this special version.
86+
ajv.addFormat('string', true)
87+
}
88+
89+
let bomSchema
90+
try {
91+
bomSchema = await readFile(bomSchemaFile, 'utf-8').then(JSON.parse)
92+
} catch (err) {
93+
++errCnt
94+
console.error('!!! JSON DECODE ERROR:', err)
95+
continue
96+
}
97+
98+
console.group(`> compile schema, log warnings ...`)
99+
try {
100+
ajv.compile(bomSchema)
101+
} catch (err) {
102+
++errCnt
103+
console.groupEnd()
104+
console.error(`!!! SCHEMA ERROR: ${err}`)
105+
continue
106+
}
107+
console.groupEnd()
108+
console.log('> SCHEMA OK.')
109+
}
110+
111+
// Exit statuses should be in the range 0 to 254.
112+
// The status 0 is used to terminate the program successfully.
113+
process.exitCode = Math.min(errCnt, 254)

tools/src/test/js/package.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"private": true,
3+
"type": "module",
4+
"engines": {
5+
"node": ">=16"
6+
},
7+
"dependencies": {
8+
"ajv": "^8.12.0",
9+
"ajv-formats": "^2.1.1",
10+
"ajv-formats-draft2019": "^1.6.1",
11+
"glob": "^10.2.6",
12+
"npm-run-all": "^4.1.5"
13+
},
14+
"devDependencies": {
15+
"@types/node": ">=16"
16+
},
17+
"scripts": {
18+
"test": "run-s test:*",
19+
"test:json-schema-lint": "node -- json-schema-lint-tests.js"
20+
}
21+
}

0 commit comments

Comments
 (0)