Skip to content

Commit 625b757

Browse files
Added schema version check
Signed-off-by: Steve Springett <[email protected]>
1 parent f0fcf97 commit 625b757

File tree

3 files changed

+108
-9
lines changed

3 files changed

+108
-9
lines changed

tools/src/main/js/linter/README.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# CycloneDX Schema Linter
22

3-
A modular linter for CycloneDX JSON schemas.
3+
A modular linter for CycloneDX JSON schemas, enforcing ISO House Style and Oxford English conventions.
44

55
## Requirements
66

77
- Node.js >= 18.0.0
8-
- aspell with en_US and en_GB-ize dictionaries
8+
- aspell with English dictionaries (provides en_US and en_GB-ize)
99

1010
```bash
1111
# Ubuntu/Debian
@@ -19,31 +19,32 @@ brew install aspell
1919

2020
```bash
2121
# Lint files
22-
node cli.js schema.json
23-
node cli.js schemas/*.schema.json
22+
node src/cli.js schema.json
23+
node src/cli.js schemas/*.schema.json
2424

2525
# Exclude or include specific checks
26-
node cli.js --exclude formatting-indent schema.json
27-
node cli.js --include description-full-stop schema.json
26+
node src/cli.js --exclude formatting-indent schema.json
27+
node src/cli.js --include description-full-stop schema.json
2828

2929
# Output formats: stylish (default), json, compact
30-
node cli.js --format json schema.json
30+
node src/cli.js --format json schema.json
3131

3232
# List available checks
33-
node cli.js --list-checks
33+
node src/cli.js --list-checks
3434
```
3535

3636
## Checks
3737

3838
| Check | Description |
3939
|-------|-------------|
40+
| `schema-draft` | Validates `$schema` is JSON Schema 2020-12 and first property |
4041
| `schema-id-pattern` | Validates `$id` matches CycloneDX URL pattern |
4142
| `schema-comment` | Validates `$comment` contains required standard notice |
4243
| `formatting-indent` | Validates 2-space indentation |
4344
| `description-full-stop` | Descriptions must end with full stop |
4445
| `meta-enum-full-stop` | `meta:enum` values must end with full stop |
4546
| `property-name-american-english` | Property names use American English |
46-
| `description-oxford-english` | Descriptions use Oxford English (British with -ize) |
47+
| `description-oxford-english` | Descriptions use Oxford English (en_GB-ize) |
4748
| `no-uppercase-rfc` | No uppercase RFC 2119 keywords (MUST, SHALL, etc.) |
4849
| `no-must-word` | Use "shall" instead of "must" per ISO style |
4950
| `additional-properties-false` | Objects must have `additionalProperties: false` |
@@ -74,3 +75,7 @@ Create `.cdxlintrc.json` in your project root:
7475
```bash
7576
npm test
7677
```
78+
79+
## Licence
80+
81+
Apache-2.0

tools/src/main/js/linter/checks/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ export async function loadAllChecks() {
3737
// Export individual check modules for direct access if needed
3838
export * from './schema-id-pattern.check.js';
3939
export * from './schema-comment.check.js';
40+
export * from './schema-draft.check.js';
4041
export * from './formatting-indent.check.js';
4142
export * from './description-full-stop.check.js';
4243
export * from './meta-enum-full-stop.check.js';
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
/**
2+
* CycloneDX Schema Linter - Schema Draft Check
3+
*
4+
* Validates that the $schema property exists, has the correct value,
5+
* and appears on the first line of the schema file.
6+
*
7+
* @license Apache-2.0
8+
*/
9+
10+
import { LintCheck, registerCheck, Severity } from '../index.js';
11+
12+
/**
13+
* Required $schema value
14+
*/
15+
const REQUIRED_SCHEMA = 'https://json-schema.org/draft/2020-12/schema';
16+
17+
/**
18+
* Check that validates the $schema property
19+
*/
20+
class SchemaDraftCheck extends LintCheck {
21+
constructor() {
22+
super(
23+
'schema-draft',
24+
'Schema Draft',
25+
'Validates that $schema is present with the correct JSON Schema draft.',
26+
Severity.ERROR
27+
);
28+
}
29+
30+
async run(schema, rawContent, config = {}) {
31+
const issues = [];
32+
33+
const requiredSchema = config.requiredSchema ?? REQUIRED_SCHEMA;
34+
35+
// Check if $schema exists
36+
if (!('$schema' in schema)) {
37+
issues.push(this.createIssue(
38+
'Schema is missing required $schema property.',
39+
'$.$schema',
40+
{ expected: requiredSchema }
41+
));
42+
return issues;
43+
}
44+
45+
// Check if $schema matches required value
46+
if (schema.$schema !== requiredSchema) {
47+
issues.push(this.createIssue(
48+
`$schema must be "${requiredSchema}".`,
49+
'$.$schema',
50+
{
51+
actual: schema.$schema,
52+
expected: requiredSchema
53+
}
54+
));
55+
}
56+
57+
// Check if $schema appears on first line
58+
const lines = rawContent.split('\n');
59+
let foundOnFirstLine = false;
60+
61+
for (let i = 0; i < lines.length && i < 3; i++) {
62+
const line = lines[i].trim();
63+
// Skip opening brace
64+
if (line === '{') continue;
65+
66+
// First non-brace line should contain $schema
67+
if (line.includes('"$schema"')) {
68+
foundOnFirstLine = true;
69+
break;
70+
} else if (line.length > 0 && line !== '{') {
71+
// Found something else first
72+
break;
73+
}
74+
}
75+
76+
if (!foundOnFirstLine) {
77+
issues.push(this.createIssue(
78+
'$schema must appear as the first property in the schema.',
79+
'$.$schema',
80+
{ suggestion: 'Move $schema to be the first property after the opening brace.' }
81+
));
82+
}
83+
84+
return issues;
85+
}
86+
}
87+
88+
// Create and register the check
89+
const check = new SchemaDraftCheck();
90+
registerCheck(check);
91+
92+
export { SchemaDraftCheck, REQUIRED_SCHEMA };
93+
export default check;

0 commit comments

Comments
 (0)