Skip to content

Commit bf78e9f

Browse files
authored
Merge pull request #106 from Adityakumar37/fix#91
2 parents 555b196 + 3f723e0 commit bf78e9f

7 files changed

Lines changed: 65 additions & 2 deletions

File tree

src/SchemaNode.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ export type Context = {
9494
getDataDefaultOptions?: TemplateOptions;
9595
/** [SHARED USING ADD REMOTE] collect unknown keywords in schemaAnnotations */
9696
withSchemaAnnotations?: boolean;
97+
strictRefs?: boolean;
9798
};
9899

99100
export interface SchemaNode extends SchemaNodeMethodsType {

src/compileSchema.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export type CompileOptions = {
3333
throwOnInvalidSchema?: boolean;
3434
/** set to true to collect unknown keywords of input schema in `node.schemaAnnotations`. Defaults to false */
3535
withSchemaAnnotations?: boolean;
36+
strictRefs?: boolean;
37+
3638
};
3739

3840
const defaultDrafts: Draft[] = [draft04, draft06, draft07, draft2019, draft2020];
@@ -69,6 +71,7 @@ export function compileSchema(schema: JsonSchema | BooleanSchema, options: Compi
6971
...copy(pick(draft, "methods", "keywords", "version", "formats", "errors")),
7072
getDataDefaultOptions: options.getDataDefaultOptions,
7173
withSchemaAnnotations: options.withSchemaAnnotations ?? false,
74+
strictRefs: options.strictRefs ?? false,
7275
drafts
7376
},
7477
...SchemaNodeMethods

src/draft06/keywords/$ref.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ function parseRef(node: SchemaNode) {
5252
function validateRef({ node, data, pointer = "#", path }: JsonSchemaValidatorParams) {
5353
const nextNode = resolveAllRefs(node, pointer, path);
5454
if (nextNode == null) {
55+
if (node.context.strictRefs) {
56+
return node.createError("unknown-ref-target-error", {
57+
ref: node.schema.$ref,
58+
pointer,
59+
schema: node.schema,
60+
value: data
61+
});
62+
}
5563
return undefined;
5664
}
5765
return validateNode(nextNode, data, pointer, path);

src/draft2019-09/keywords/$ref.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,16 @@ export function resolveRef(this: SchemaNode, { pointer, path }: { pointer?: stri
9393
function validateRef({ node, data, pointer = "#", path }: JsonSchemaValidatorParams) {
9494
const nextNode = node.resolveRef({ pointer, path });
9595
if (nextNode != null) {
96-
// recursively resolveRef and validate
9796
return validateNode(nextNode, data, pointer, path);
9897
}
98+
if (node.context.strictRefs) {
99+
return node.createError("unknown-ref-target-error", {
100+
ref: node.schema.$ref ?? node.schema.$recursiveRef,
101+
pointer,
102+
schema: node.schema,
103+
value: data
104+
});
105+
}
99106
}
100107

101108
// 1. https://json-schema.org/draft/2019-09/json-schema-core#scopes

src/errors/errors.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,5 +77,6 @@ export const errors = {
7777
"deprecated-warning": "Value at `{{pointer}}` is deprecated",
7878
// schema validation
7979
"schema-error": "Invalid schema found at {{pointer}}: {{message}}",
80-
"unknown-keyword-warning": "Keyword '{{value}}' is not a valid keyword to draft '{{draft}}'"
80+
"unknown-keyword-warning": "Keyword '{{value}}' is not a valid keyword to draft '{{draft}}'",
81+
"unknown-ref-target-error": "Could not resolve $ref '{{ref}}' from '{{pointer}}'",
8182
};

src/keywords/$ref.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,18 @@ export function reduceRef({ node, data, key, pointer, path }: JsonSchemaReducerP
9090
if (node == null) {
9191
return;
9292
}
93+
9394

9495
const resolvedNode = node.resolveRef({ pointer, path });
9596
if (resolvedNode == null) {
97+
if (node.context.strictRefs) {
98+
return node.createError("unknown-ref-target-error", {
99+
ref: node.schema.$ref ?? node.schema.$dynamicRef,
100+
pointer,
101+
schema: node.schema,
102+
value: data
103+
});
104+
}
96105
return;
97106
}
98107

@@ -129,6 +138,14 @@ function validateRef({ node, data, pointer = "#", path }: JsonSchemaValidatorPar
129138
// recursively resolveRef and validate
130139
return validateNode(nextNode, data, pointer, path);
131140
}
141+
if (node.context.strictRefs) {
142+
return node.createError("unknown-ref-target-error", {
143+
ref: node.schema.$ref ?? node.schema.$dynamicRef,
144+
pointer,
145+
schema: node.schema,
146+
value: data
147+
});
148+
}
132149
}
133150

134151
// 1. https://json-schema.org/draft/2019-09/json-schema-core#scopes

src/tests/issues/issue82.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,30 @@ describe("issue#82", () => {
6868

6969
assert.equal(valid, false);
7070
});
71+
it("should return an error when $ref cannot be resolved and strictRefs is true", () => {
72+
const schema = compileSchema({
73+
$schema: "https://json-schema.org/draft/2020-12/schema",
74+
type: "object",
75+
properties: {
76+
value: { $ref: "#/$defs/nonexistent" }
77+
}
78+
}, { strictRefs: true });
79+
80+
const { valid, errors } = schema.validate({ value: 123 });
81+
assert.equal(valid, false);
82+
assert.equal(errors[0].code, "unknown-ref-target-error");
83+
});
84+
85+
it("should silently pass when $ref cannot be resolved and strictRefs is false (default)", () => {
86+
const schema = compileSchema({
87+
type: "object",
88+
properties: {
89+
value: { $ref: "#/$defs/nonexistent" }
90+
}
91+
});
92+
93+
const { valid } = schema.validate({ value: 123 });
94+
assert.equal(valid, true);
95+
});
96+
7197
});

0 commit comments

Comments
 (0)