Skip to content

Commit f0c447b

Browse files
Moved bundler
Signed-off-by: Steve Springett <[email protected]>
1 parent b8abb0c commit f0c447b

File tree

3 files changed

+38
-20
lines changed

3 files changed

+38
-20
lines changed

.github/workflows/bundle_2.0_schemas.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
- 2.0-dev-threatmodeling
88
paths:
99
- 'schema/2.0/**/*.schema.json'
10-
- 'tools/src/main/js/bundle-schemas.js'
10+
- 'tools/src/main/js/bundler/bundle-schemas.js'
1111
workflow_dispatch: # Allows manual trigger
1212

1313
jobs:
@@ -29,15 +29,15 @@ jobs:
2929
node-version: '20'
3030

3131
- name: Install dependencies
32-
working-directory: tools/src/main/js
32+
working-directory: tools/src/main/js/bundler
3333
run: npm install
3434

3535
- name: Bundle schemas
36-
working-directory: tools/src/main/js
36+
working-directory: tools/src/main/js/bundler
3737
run: |
3838
node bundle-schemas.js \
39-
../../../../schema/2.0/model \
40-
../../../../schema/2.0/cyclonedx-2.0.schema.json
39+
../../../../../schema/2.0/model \
40+
../../../../../schema/2.0/cyclonedx-2.0.schema.json
4141
4242
- name: Check for changes and commit
4343
run: |

tools/src/main/js/bundle-schemas.js renamed to tools/src/main/js/bundler/bundle-schemas.js

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
const fs = require('fs').promises;
44
const path = require('path');
55

6+
// Default list of external schema files to bypass for validation and rewriting.
7+
// This constant is used as the default value for ref exceptions; can be overridden via options.refExceptions.
8+
const DEFAULT_REF_EXCEPTION_FILES = [
9+
'spdx.schema.json',
10+
'cryptography-defs.schema.json',
11+
'jsf-0.82.schema.json'
12+
];
13+
614
function isObject(value) {
715
return typeof value === 'object' && value !== null;
816
}
@@ -61,13 +69,13 @@ function collectRefKeywords(obj, keys, predicate, pathStack = []) {
6169
/**
6270
* Recursively walks through an object and rewrites $ref paths
6371
*/
64-
function rewriteRefs(obj, schemaFiles, defsKeyword, currentSchemaName) {
72+
function rewriteRefs(obj, schemaFiles, defsKeyword, currentSchemaName, refExceptionSet) {
6573
if (typeof obj !== 'object' || obj === null) {
6674
return obj;
6775
}
6876

6977
if (Array.isArray(obj)) {
70-
return obj.map(item => rewriteRefs(item, schemaFiles, defsKeyword, currentSchemaName));
78+
return obj.map(item => rewriteRefs(item, schemaFiles, defsKeyword, currentSchemaName, refExceptionSet));
7179
}
7280

7381
const newObj = {};
@@ -82,17 +90,22 @@ function rewriteRefs(obj, schemaFiles, defsKeyword, currentSchemaName) {
8290
const basename = path.basename(filename);
8391
const schemaName = basename.replace('.schema.json', '');
8492

85-
// Normalize fragment: drop leading '#' and optional leading '/'
86-
let fragPath = '';
87-
if (fragment) {
88-
fragPath = fragment.startsWith('#') ? fragment.slice(1) : fragment;
89-
if (fragPath.startsWith('/')) fragPath = fragPath.slice(1);
93+
// If the target file is in the exception list, leave the ref as-is
94+
if (refExceptionSet && refExceptionSet.has(basename.toLowerCase())) {
95+
newObj[key] = value;
96+
} else {
97+
// Normalize fragment: drop leading '#' and optional leading '/'
98+
let fragPath = '';
99+
if (fragment) {
100+
fragPath = fragment.startsWith('#') ? fragment.slice(1) : fragment;
101+
if (fragPath.startsWith('/')) fragPath = fragPath.slice(1);
102+
}
103+
104+
// Rewrite to point to the bundled schema's definitions
105+
newObj[key] = fragPath
106+
? `#/${defsKeyword}/${schemaName}/${fragPath}`
107+
: `#/${defsKeyword}/${schemaName}`;
90108
}
91-
92-
// Rewrite to point to the bundled schema's definitions
93-
newObj[key] = fragPath
94-
? `#/${defsKeyword}/${schemaName}/${fragPath}`
95-
: `#/${defsKeyword}/${schemaName}`;
96109
}
97110
// Case 2: Internal reference within the same schema (starts with #)
98111
else if (value.startsWith('#')) {
@@ -104,7 +117,7 @@ function rewriteRefs(obj, schemaFiles, defsKeyword, currentSchemaName) {
104117
newObj[key] = value;
105118
}
106119
} else {
107-
newObj[key] = rewriteRefs(value, schemaFiles, defsKeyword, currentSchemaName);
120+
newObj[key] = rewriteRefs(value, schemaFiles, defsKeyword, currentSchemaName, refExceptionSet);
108121
}
109122
}
110123
return newObj;
@@ -218,17 +231,22 @@ async function bundleSchemas(modelsDirectory, rootSchemaPath, options = {}) {
218231
console.log(`\nUsing schema version: ${schemaVersion}`);
219232
console.log(`Using keyword: ${defsKeyword}`);
220233

234+
// Build exception set for external refs not to check or rewrite
235+
const refExceptionSet = new Set((options.refExceptions || DEFAULT_REF_EXCEPTION_FILES).map(s => s.toLowerCase()));
236+
221237
// Pre-check: external file $ref targets must exist among loaded schemas
222238
console.log('Validating external $ref targets...');
223239
const allowedFiles = new Set([...schemaFiles, rootSchemaFilename]);
224240
for (const [name, schema] of Object.entries(schemas)) {
225241
// Only $ref can be external; $dynamicRef/$recursiveRef are JSON Pointers by spec
226-
const refs = collectRefKeywords(schema, ['$ref'], (v) => /^(.+\.schema\.json)(#.*)?$/.test(v));
242+
const refs = collectRefKeywords(schema, ['$ref'], (v) => /^(\.?.*\.schema\.json)(#.*)?$/.test(v));
227243
for (const { ref, key, path: refPath } of refs) {
228244
const m = ref.match(/^(.+\.schema\.json)(#.*)?$/);
229245
if (!m) continue;
230246
const target = m[1];
231247
const base = path.basename(target);
248+
// Skip validation if target file is in exceptions
249+
if (refExceptionSet.has(base.toLowerCase())) continue;
232250
if (!allowedFiles.has(base)) {
233251
throw new Error(`Unresolved external ${key} target file '${target}' referenced from schema '${name}' at '${refPath}'`);
234252
}
@@ -241,7 +259,7 @@ async function bundleSchemas(modelsDirectory, rootSchemaPath, options = {}) {
241259
const rewrittenDefinitions = {};
242260
for (const [name, schema] of Object.entries(schemas)) {
243261
console.log(` Rewriting refs in ${name}...`);
244-
rewrittenDefinitions[name] = rewriteRefs(schema, [...schemaFiles, rootSchemaFilename], defsKeyword, name);
262+
rewrittenDefinitions[name] = rewriteRefs(schema, [...schemaFiles, rootSchemaFilename], defsKeyword, name, refExceptionSet);
245263
}
246264

247265
// Get the rewritten root schema

0 commit comments

Comments
 (0)