@uncefact/untp-utils 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -2
- package/artefacts/context/untp/0.6.0/dcc.json +1241 -0
- package/artefacts/context/untp/0.6.0/dfr.json +619 -0
- package/artefacts/context/untp/0.6.0/dia.json +162 -0
- package/artefacts/context/untp/0.6.0/dpp.json +1078 -0
- package/artefacts/context/untp/0.6.0/dte.json +1285 -0
- package/artefacts/context/untp/0.6.1/dcc.json +1388 -0
- package/artefacts/context/untp/0.6.1/dfr.json +690 -0
- package/artefacts/context/untp/0.6.1/dia.json +196 -0
- package/artefacts/context/untp/0.6.1/dpp.json +1164 -0
- package/artefacts/context/untp/0.6.1/dte.json +1340 -0
- package/artefacts/context/untp/0.7.0/untp.json +3493 -0
- package/artefacts/context/vcdm/2/credentials.json +301 -0
- package/artefacts/manifest.json +214 -0
- package/artefacts/schema/untp/0.6.0/dcc.json +1131 -0
- package/artefacts/schema/untp/0.6.0/dfr.json +1028 -0
- package/artefacts/schema/untp/0.6.0/dia.json +236 -0
- package/artefacts/schema/untp/0.6.0/dpp.json +1219 -0
- package/artefacts/schema/untp/0.6.0/dte.json +845 -0
- package/artefacts/schema/untp/0.6.1/dcc.json +1196 -0
- package/artefacts/schema/untp/0.6.1/dfr.json +1037 -0
- package/artefacts/schema/untp/0.6.1/dia.json +236 -0
- package/artefacts/schema/untp/0.6.1/dpp.json +1229 -0
- package/artefacts/schema/untp/0.6.1/dte.json +845 -0
- package/artefacts/schema/untp/0.7.0/cvc.json +701 -0
- package/artefacts/schema/untp/0.7.0/dcc.json +1133 -0
- package/artefacts/schema/untp/0.7.0/dfr.json +1245 -0
- package/artefacts/schema/untp/0.7.0/dia.json +443 -0
- package/artefacts/schema/untp/0.7.0/dpp.json +1300 -0
- package/artefacts/schema/untp/0.7.0/dte.json +1226 -0
- package/artefacts/schema/untp/0.7.0/linkset.json +119 -0
- package/artefacts/schema/vcdm/2/verifiable-credential.json +318 -0
- package/build/artefacts/detect-version-from-context.d.ts +46 -0
- package/build/artefacts/detect-version-from-context.js +77 -0
- package/build/artefacts/detect-version-from-context.js.map +1 -0
- package/build/artefacts/detect-version-from-context.test.d.ts +1 -0
- package/build/artefacts/detect-version-from-context.test.js +69 -0
- package/build/artefacts/detect-version-from-context.test.js.map +1 -0
- package/build/artefacts/index.d.ts +3 -0
- package/build/artefacts/index.js +4 -0
- package/build/artefacts/index.js.map +1 -0
- package/build/artefacts/urls.d.ts +85 -0
- package/build/artefacts/urls.js +141 -0
- package/build/artefacts/urls.js.map +1 -0
- package/build/artefacts/urls.test.d.ts +1 -0
- package/build/artefacts/urls.test.js +99 -0
- package/build/artefacts/urls.test.js.map +1 -0
- package/build/artefacts/version.d.ts +9 -0
- package/build/artefacts/version.js +17 -0
- package/build/artefacts/version.js.map +1 -0
- package/build/artefacts/version.test.d.ts +1 -0
- package/build/artefacts/version.test.js +16 -0
- package/build/artefacts/version.test.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dcc.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dcc.js +1244 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dcc.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dfr.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dfr.js +622 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dfr.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dia.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dia.js +165 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dia.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dpp.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dpp.js +1081 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dpp.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dte.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dte.js +1288 -0
- package/build/bundle/artefacts/context/untp/0.6.0/dte.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dcc.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dcc.js +1391 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dcc.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dfr.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dfr.js +693 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dfr.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dia.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dia.js +199 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dia.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dpp.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dpp.js +1167 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dpp.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dte.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dte.js +1343 -0
- package/build/bundle/artefacts/context/untp/0.6.1/dte.js.map +1 -0
- package/build/bundle/artefacts/context/untp/0.7.0/untp.d.ts +2 -0
- package/build/bundle/artefacts/context/untp/0.7.0/untp.js +3496 -0
- package/build/bundle/artefacts/context/untp/0.7.0/untp.js.map +1 -0
- package/build/bundle/artefacts/context/vcdm/2/credentials.d.ts +2 -0
- package/build/bundle/artefacts/context/vcdm/2/credentials.js +304 -0
- package/build/bundle/artefacts/context/vcdm/2/credentials.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dcc.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dcc.js +1134 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dcc.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dfr.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dfr.js +1031 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dfr.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dia.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dia.js +239 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dia.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dpp.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dpp.js +1222 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dpp.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dte.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dte.js +848 -0
- package/build/bundle/artefacts/schema/untp/0.6.0/dte.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dcc.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dcc.js +1199 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dcc.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dfr.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dfr.js +1040 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dfr.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dia.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dia.js +239 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dia.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dpp.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dpp.js +1232 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dpp.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dte.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dte.js +848 -0
- package/build/bundle/artefacts/schema/untp/0.6.1/dte.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/cvc.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/cvc.js +704 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/cvc.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dcc.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dcc.js +1136 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dcc.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dfr.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dfr.js +1248 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dfr.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dia.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dia.js +446 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dia.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dpp.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dpp.js +1303 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dpp.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dte.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dte.js +1229 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/dte.js.map +1 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/linkset.d.ts +2 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/linkset.js +122 -0
- package/build/bundle/artefacts/schema/untp/0.7.0/linkset.js.map +1 -0
- package/build/bundle/artefacts/schema/vcdm/2/verifiable-credential.d.ts +2 -0
- package/build/bundle/artefacts/schema/vcdm/2/verifiable-credential.js +321 -0
- package/build/bundle/artefacts/schema/vcdm/2/verifiable-credential.js.map +1 -0
- package/build/bundle/fallback.d.ts +41 -0
- package/build/bundle/fallback.js +62 -0
- package/build/bundle/fallback.js.map +1 -0
- package/build/bundle/fallback.test.d.ts +1 -0
- package/build/bundle/fallback.test.js +124 -0
- package/build/bundle/fallback.test.js.map +1 -0
- package/build/bundle/index.d.ts +4 -0
- package/build/bundle/index.js +77 -0
- package/build/bundle/index.js.map +1 -0
- package/build/bundle/lookup.d.ts +14 -0
- package/build/bundle/lookup.js +40 -0
- package/build/bundle/lookup.js.map +1 -0
- package/build/bundle/lookup.test.d.ts +1 -0
- package/build/bundle/lookup.test.js +70 -0
- package/build/bundle/lookup.test.js.map +1 -0
- package/build/bundle/public.d.ts +25 -0
- package/build/bundle/public.js +56 -0
- package/build/bundle/public.js.map +1 -0
- package/build/bundle/public.test.d.ts +1 -0
- package/build/bundle/public.test.js +35 -0
- package/build/bundle/public.test.js.map +1 -0
- package/build/bundle/refresh-classify.test.d.ts +1 -0
- package/build/bundle/refresh-classify.test.js +20 -0
- package/build/bundle/refresh-classify.test.js.map +1 -0
- package/build/bundle/refresh-main.test.d.ts +1 -0
- package/build/bundle/refresh-main.test.js +98 -0
- package/build/bundle/refresh-main.test.js.map +1 -0
- package/build/cache/in-memory-ttl-cache.d.ts +2 -0
- package/build/cache/in-memory-ttl-cache.js +76 -0
- package/build/cache/in-memory-ttl-cache.js.map +1 -0
- package/build/cache/in-memory-ttl-cache.test.d.ts +1 -0
- package/build/cache/in-memory-ttl-cache.test.js +236 -0
- package/build/cache/in-memory-ttl-cache.test.js.map +1 -0
- package/build/cache/index.d.ts +2 -0
- package/build/cache/index.js +2 -0
- package/build/cache/index.js.map +1 -0
- package/build/cache/ttl-cache.d.ts +22 -0
- package/build/cache/ttl-cache.js +2 -0
- package/build/cache/ttl-cache.js.map +1 -0
- package/build/common/as-date-time.d.ts +13 -0
- package/build/common/as-date-time.js +44 -0
- package/build/common/as-date-time.js.map +1 -0
- package/build/common/as-date-time.test.d.ts +1 -0
- package/build/common/as-date-time.test.js +42 -0
- package/build/common/as-date-time.test.js.map +1 -0
- package/build/common/as-non-empty-string.d.ts +14 -0
- package/build/common/as-non-empty-string.js +20 -0
- package/build/common/as-non-empty-string.js.map +1 -0
- package/build/common/as-non-empty-string.test.d.ts +1 -0
- package/build/common/as-non-empty-string.test.js +30 -0
- package/build/common/as-non-empty-string.test.js.map +1 -0
- package/build/common/index.d.ts +4 -0
- package/build/common/index.js +5 -0
- package/build/common/index.js.map +1 -0
- package/build/common/require-string.d.ts +12 -0
- package/build/common/require-string.js +27 -0
- package/build/common/require-string.js.map +1 -0
- package/build/common/require-string.test.d.ts +1 -0
- package/build/common/require-string.test.js +53 -0
- package/build/common/require-string.test.js.map +1 -0
- package/build/common/validity-window.d.ts +45 -0
- package/build/common/validity-window.js +68 -0
- package/build/common/validity-window.js.map +1 -0
- package/build/common/validity-window.test.d.ts +1 -0
- package/build/common/validity-window.test.js +67 -0
- package/build/common/validity-window.test.js.map +1 -0
- package/build/conformity-vocabulary/codes.d.ts +10 -0
- package/build/conformity-vocabulary/codes.js +10 -0
- package/build/conformity-vocabulary/codes.js.map +1 -0
- package/build/conformity-vocabulary/errors.d.ts +44 -0
- package/build/conformity-vocabulary/errors.js +68 -0
- package/build/conformity-vocabulary/errors.js.map +1 -0
- package/build/conformity-vocabulary/index.d.ts +6 -0
- package/build/conformity-vocabulary/index.js +7 -0
- package/build/conformity-vocabulary/index.js.map +1 -0
- package/build/conformity-vocabulary/parse-conformity-catalogue.d.ts +26 -0
- package/build/conformity-vocabulary/parse-conformity-catalogue.js +106 -0
- package/build/conformity-vocabulary/parse-conformity-catalogue.js.map +1 -0
- package/build/conformity-vocabulary/parse-conformity-catalogue.test.d.ts +1 -0
- package/build/conformity-vocabulary/parse-conformity-catalogue.test.js +327 -0
- package/build/conformity-vocabulary/parse-conformity-catalogue.test.js.map +1 -0
- package/build/conformity-vocabulary/parse-conformity-scheme.d.ts +28 -0
- package/build/conformity-vocabulary/parse-conformity-scheme.js +47 -0
- package/build/conformity-vocabulary/parse-conformity-scheme.js.map +1 -0
- package/build/conformity-vocabulary/parse-conformity-scheme.test.d.ts +1 -0
- package/build/conformity-vocabulary/parse-conformity-scheme.test.js +284 -0
- package/build/conformity-vocabulary/parse-conformity-scheme.test.js.map +1 -0
- package/build/conformity-vocabulary/parsers/v0-7-0.parser.d.ts +19 -0
- package/build/conformity-vocabulary/parsers/v0-7-0.parser.js +219 -0
- package/build/conformity-vocabulary/parsers/v0-7-0.parser.js.map +1 -0
- package/build/conformity-vocabulary/types.d.ts +180 -0
- package/build/conformity-vocabulary/types.js +2 -0
- package/build/conformity-vocabulary/types.js.map +1 -0
- package/build/conformity-vocabulary/validate-conformity-claim.d.ts +26 -0
- package/build/conformity-vocabulary/validate-conformity-claim.js +165 -0
- package/build/conformity-vocabulary/validate-conformity-claim.js.map +1 -0
- package/build/conformity-vocabulary/validate-conformity-claim.test.d.ts +1 -0
- package/build/conformity-vocabulary/validate-conformity-claim.test.js +478 -0
- package/build/conformity-vocabulary/validate-conformity-claim.test.js.map +1 -0
- package/build/http-headers/index.d.ts +5 -0
- package/build/http-headers/index.js +6 -0
- package/build/http-headers/index.js.map +1 -0
- package/build/http-headers/is-safe-header-value.d.ts +10 -0
- package/build/http-headers/is-safe-header-value.js +15 -0
- package/build/http-headers/is-safe-header-value.js.map +1 -0
- package/build/http-headers/is-safe-header-value.test.d.ts +1 -0
- package/build/http-headers/is-safe-header-value.test.js +37 -0
- package/build/http-headers/is-safe-header-value.test.js.map +1 -0
- package/build/http-headers/parse-entity-tag.d.ts +9 -0
- package/build/http-headers/parse-entity-tag.js +18 -0
- package/build/http-headers/parse-entity-tag.js.map +1 -0
- package/build/http-headers/parse-entity-tag.test.d.ts +1 -0
- package/build/http-headers/parse-entity-tag.test.js +41 -0
- package/build/http-headers/parse-entity-tag.test.js.map +1 -0
- package/build/http-headers/parse-imf-date.d.ts +13 -0
- package/build/http-headers/parse-imf-date.js +23 -0
- package/build/http-headers/parse-imf-date.js.map +1 -0
- package/build/http-headers/parse-imf-date.test.d.ts +1 -0
- package/build/http-headers/parse-imf-date.test.js +31 -0
- package/build/http-headers/parse-imf-date.test.js.map +1 -0
- package/build/http-headers/parse-media-type.d.ts +8 -0
- package/build/http-headers/parse-media-type.js +21 -0
- package/build/http-headers/parse-media-type.js.map +1 -0
- package/build/http-headers/parse-media-type.test.d.ts +1 -0
- package/build/http-headers/parse-media-type.test.js +39 -0
- package/build/http-headers/parse-media-type.test.js.map +1 -0
- package/build/http-headers/user-agent.d.ts +35 -0
- package/build/http-headers/user-agent.js +45 -0
- package/build/http-headers/user-agent.js.map +1 -0
- package/build/http-headers/user-agent.test.d.ts +1 -0
- package/build/http-headers/user-agent.test.js +27 -0
- package/build/http-headers/user-agent.test.js.map +1 -0
- package/build/index.d.ts +1 -1
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/build/loaders/errors.d.ts +21 -0
- package/build/loaders/errors.js +45 -0
- package/build/loaders/errors.js.map +1 -0
- package/build/loaders/index.d.ts +5 -0
- package/build/loaders/index.js +5 -0
- package/build/loaders/index.js.map +1 -0
- package/build/loaders/jsonld-document-loader.d.ts +48 -0
- package/build/loaders/jsonld-document-loader.js +43 -0
- package/build/loaders/jsonld-document-loader.js.map +1 -0
- package/build/loaders/jsonld-document-loader.test.d.ts +1 -0
- package/build/loaders/jsonld-document-loader.test.js +111 -0
- package/build/loaders/jsonld-document-loader.test.js.map +1 -0
- package/build/loaders/schema-loader.d.ts +31 -0
- package/build/loaders/schema-loader.js +83 -0
- package/build/loaders/schema-loader.js.map +1 -0
- package/build/loaders/schema-loader.ssrf.test.d.ts +1 -0
- package/build/loaders/schema-loader.ssrf.test.js +55 -0
- package/build/loaders/schema-loader.ssrf.test.js.map +1 -0
- package/build/loaders/schema-loader.test.d.ts +1 -0
- package/build/loaders/schema-loader.test.js +217 -0
- package/build/loaders/schema-loader.test.js.map +1 -0
- package/build/multibase-digest/multibase-digest.d.ts +23 -0
- package/build/multibase-digest/multibase-digest.js +40 -0
- package/build/multibase-digest/multibase-digest.js.map +1 -1
- package/build/multibase-digest/multibase-digest.test.js +46 -0
- package/build/multibase-digest/multibase-digest.test.js.map +1 -1
- package/build/node/errors.d.ts +47 -0
- package/build/node/errors.js +86 -0
- package/build/node/errors.js.map +1 -0
- package/build/node/index.d.ts +3 -0
- package/build/node/index.js +4 -0
- package/build/node/index.js.map +1 -0
- package/build/node/is-private-ip.d.ts +45 -0
- package/build/node/is-private-ip.fail-closed.test.d.ts +1 -0
- package/build/node/is-private-ip.fail-closed.test.js +36 -0
- package/build/node/is-private-ip.fail-closed.test.js.map +1 -0
- package/build/node/is-private-ip.js +203 -0
- package/build/node/is-private-ip.js.map +1 -0
- package/build/node/is-private-ip.test.d.ts +1 -0
- package/build/node/is-private-ip.test.js +124 -0
- package/build/node/is-private-ip.test.js.map +1 -0
- package/build/node/validate-public-url.d.ts +81 -0
- package/build/node/validate-public-url.js +128 -0
- package/build/node/validate-public-url.js.map +1 -0
- package/build/node/validate-public-url.test.d.ts +1 -0
- package/build/node/validate-public-url.test.js +324 -0
- package/build/node/validate-public-url.test.js.map +1 -0
- package/build/resolvers/errors.d.ts +73 -0
- package/build/resolvers/errors.js +133 -0
- package/build/resolvers/errors.js.map +1 -0
- package/build/resolvers/index.d.ts +5 -0
- package/build/resolvers/index.js +6 -0
- package/build/resolvers/index.js.map +1 -0
- package/build/resolvers/resolve-document-if-changed.d.ts +48 -0
- package/build/resolvers/resolve-document-if-changed.js +42 -0
- package/build/resolvers/resolve-document-if-changed.js.map +1 -0
- package/build/resolvers/resolve-document-if-changed.test.d.ts +1 -0
- package/build/resolvers/resolve-document-if-changed.test.js +116 -0
- package/build/resolvers/resolve-document-if-changed.test.js.map +1 -0
- package/build/resolvers/resolve-document.d.ts +106 -0
- package/build/resolvers/resolve-document.integration.test.d.ts +1 -0
- package/build/resolvers/resolve-document.integration.test.js +108 -0
- package/build/resolvers/resolve-document.integration.test.js.map +1 -0
- package/build/resolvers/resolve-document.js +319 -0
- package/build/resolvers/resolve-document.js.map +1 -0
- package/build/resolvers/resolve-document.test.d.ts +1 -0
- package/build/resolvers/resolve-document.test.js +749 -0
- package/build/resolvers/resolve-document.test.js.map +1 -0
- package/build/resolvers/resolve-json-document.d.ts +43 -0
- package/build/resolvers/resolve-json-document.js +43 -0
- package/build/resolvers/resolve-json-document.js.map +1 -0
- package/build/resolvers/resolve-json-document.test.d.ts +1 -0
- package/build/resolvers/resolve-json-document.test.js +78 -0
- package/build/resolvers/resolve-json-document.test.js.map +1 -0
- package/build/structured-error.d.ts +49 -0
- package/build/structured-error.js +17 -0
- package/build/structured-error.js.map +1 -0
- package/build/structured-error.test.d.ts +1 -0
- package/build/structured-error.test.js +112 -0
- package/build/structured-error.test.js.map +1 -0
- package/build/validation/describe-jsonld-failure.d.ts +90 -0
- package/build/validation/describe-jsonld-failure.js +171 -0
- package/build/validation/describe-jsonld-failure.js.map +1 -0
- package/build/validation/describe-jsonld-failure.test.d.ts +1 -0
- package/build/validation/describe-jsonld-failure.test.js +226 -0
- package/build/validation/describe-jsonld-failure.test.js.map +1 -0
- package/build/validation/errors.d.ts +40 -0
- package/build/validation/errors.js +75 -0
- package/build/validation/errors.js.map +1 -0
- package/build/validation/index.d.ts +5 -0
- package/build/validation/index.js +5 -0
- package/build/validation/index.js.map +1 -0
- package/build/validation/validate-against-schemas.d.ts +22 -0
- package/build/validation/validate-against-schemas.js +65 -0
- package/build/validation/validate-against-schemas.js.map +1 -0
- package/build/validation/validate-against-schemas.test.d.ts +1 -0
- package/build/validation/validate-against-schemas.test.js +169 -0
- package/build/validation/validate-against-schemas.test.js.map +1 -0
- package/build/validation/validate-jsonld.context-cache.test.d.ts +1 -0
- package/build/validation/validate-jsonld.context-cache.test.js +32 -0
- package/build/validation/validate-jsonld.context-cache.test.js.map +1 -0
- package/build/validation/validate-jsonld.d.ts +59 -0
- package/build/validation/validate-jsonld.js +126 -0
- package/build/validation/validate-jsonld.js.map +1 -0
- package/build/validation/validate-jsonld.ssrf.test.d.ts +1 -0
- package/build/validation/validate-jsonld.ssrf.test.js +108 -0
- package/build/validation/validate-jsonld.ssrf.test.js.map +1 -0
- package/build/validation/validate-jsonld.test.d.ts +1 -0
- package/build/validation/validate-jsonld.test.js +257 -0
- package/build/validation/validate-jsonld.test.js.map +1 -0
- package/package.json +86 -5
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import Ajv2020 from 'ajv/dist/2020.js';
|
|
2
|
+
import addFormats from 'ajv-formats';
|
|
3
|
+
import { SchemaCompilationFailedError, SchemaFetchFailedError, SchemaPayloadError } from './errors.js';
|
|
4
|
+
function buildAjv() {
|
|
5
|
+
const ajv = new Ajv2020({ strict: false, allErrors: true, verbose: true });
|
|
6
|
+
addFormats(ajv);
|
|
7
|
+
return ajv;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Validates a payload against one or more JSON Schemas. Ajv runs with
|
|
11
|
+
* `allErrors: true` so a single payload surfaces every validation failure
|
|
12
|
+
* per schema, and payload failures accumulate across all schemas.
|
|
13
|
+
*
|
|
14
|
+
* Fetch and compile failures are fail-fast: the first schema that cannot
|
|
15
|
+
* be loaded or compiled aborts the run. Only payload-invalid failures
|
|
16
|
+
* accumulate.
|
|
17
|
+
*
|
|
18
|
+
* @throws {SchemaFetchFailedError} if a schema URL cannot be loaded.
|
|
19
|
+
* @throws {SchemaCompilationFailedError} if Ajv refuses a schema.
|
|
20
|
+
* @throws {SchemaPayloadError} carrying every Ajv `allErrors` failure in
|
|
21
|
+
* `failures[]` if any payload validation failed.
|
|
22
|
+
*/
|
|
23
|
+
export async function validateAgainstSchemas(payload, schemas, loader) {
|
|
24
|
+
const failures = [];
|
|
25
|
+
const ajv = buildAjv();
|
|
26
|
+
for (const ref of schemas) {
|
|
27
|
+
let schemaDoc;
|
|
28
|
+
let identifier;
|
|
29
|
+
if (typeof ref === 'string') {
|
|
30
|
+
identifier = ref;
|
|
31
|
+
try {
|
|
32
|
+
schemaDoc = await loader.load(ref);
|
|
33
|
+
}
|
|
34
|
+
catch (cause) {
|
|
35
|
+
throw new SchemaFetchFailedError(ref, cause);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
identifier = '<inline schema>';
|
|
40
|
+
schemaDoc = ref;
|
|
41
|
+
}
|
|
42
|
+
let validate;
|
|
43
|
+
try {
|
|
44
|
+
validate = ajv.compile(schemaDoc);
|
|
45
|
+
}
|
|
46
|
+
catch (cause) {
|
|
47
|
+
throw new SchemaCompilationFailedError(identifier, cause);
|
|
48
|
+
}
|
|
49
|
+
if (!validate(payload) && validate.errors) {
|
|
50
|
+
for (const ajvError of validate.errors) {
|
|
51
|
+
failures.push({
|
|
52
|
+
code: 'schema.payload-invalid',
|
|
53
|
+
message: ajvError.message ?? 'Schema validation failed.',
|
|
54
|
+
pointer: ajvError.instancePath || '',
|
|
55
|
+
received: ajvError.data,
|
|
56
|
+
expected: ajvError.params,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (failures.length > 0) {
|
|
62
|
+
throw new SchemaPayloadError(failures);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=validate-against-schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-against-schemas.js","sourceRoot":"","sources":["../../src/validation/validate-against-schemas.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,kBAAkB,CAAC;AACvC,OAAO,UAAU,MAAM,aAAa,CAAC;AAGrC,OAAO,EAAE,4BAA4B,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEvG,SAAS,QAAQ;IACf,MAAM,GAAG,GAAG,IAAI,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,UAAU,CAAC,GAAG,CAAC,CAAC;IAChB,OAAO,GAAG,CAAC;AACb,CAAC;AASD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,OAAgB,EAChB,OAA0B,EAC1B,MAAoB;IAEpB,MAAM,QAAQ,GAAwB,EAAE,CAAC;IACzC,MAAM,GAAG,GAAG,QAAQ,EAAE,CAAC;IAEvB,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,IAAI,SAAiB,CAAC;QACtB,IAAI,UAAkB,CAAC;QAEvB,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5B,UAAU,GAAG,GAAG,CAAC;YACjB,IAAI,CAAC;gBACH,SAAS,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACrC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,IAAI,sBAAsB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;aAAM,CAAC;YACN,UAAU,GAAG,iBAAiB,CAAC;YAC/B,SAAS,GAAG,GAAG,CAAC;QAClB,CAAC;QAED,IAAI,QAAwC,CAAC;QAC7C,IAAI,CAAC;YACH,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,4BAA4B,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;QAC5D,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;YAC1C,KAAK,MAAM,QAAQ,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;gBACvC,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,wBAAwB;oBAC9B,OAAO,EAAE,QAAQ,CAAC,OAAO,IAAI,2BAA2B;oBACxD,OAAO,EAAE,QAAQ,CAAC,YAAY,IAAI,EAAE;oBACpC,QAAQ,EAAE,QAAQ,CAAC,IAAI;oBACvB,QAAQ,EAAE,QAAQ,CAAC,MAAM;iBAC1B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,MAAM,IAAI,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IACzC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
import { ResolverHttpError, ResolverInvalidJsonError } from '../resolvers/errors.js';
|
|
3
|
+
import { SchemaCompilationFailedError, SchemaFetchFailedError, SchemaPayloadError, SchemaValidationError, } from './errors.js';
|
|
4
|
+
const resolveJsonDocument = jest.fn();
|
|
5
|
+
// The real schema loader fetches through the guarded resolver; mock that
|
|
6
|
+
// boundary (keeping the real error classes) so this suite exercises the real
|
|
7
|
+
// loader + cache + Ajv without touching the network.
|
|
8
|
+
jest.unstable_mockModule('../resolvers/index.js', () => ({
|
|
9
|
+
resolveJsonDocument,
|
|
10
|
+
ResolverHttpError,
|
|
11
|
+
ResolverInvalidJsonError,
|
|
12
|
+
}));
|
|
13
|
+
const { createInMemoryTtlCache } = await import('../cache/in-memory-ttl-cache.js');
|
|
14
|
+
const { createSchemaLoader } = await import('../loaders/schema-loader.js');
|
|
15
|
+
const { validateAgainstSchemas } = await import('./validate-against-schemas.js');
|
|
16
|
+
const SCHEMA_URL_NAME = 'https://example.com/name-schema.json';
|
|
17
|
+
const SCHEMA_URL_AGE = 'https://example.com/age-schema.json';
|
|
18
|
+
const NAME_SCHEMA = {
|
|
19
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: { name: { type: 'string' } },
|
|
22
|
+
required: ['name'],
|
|
23
|
+
};
|
|
24
|
+
const AGE_SCHEMA = {
|
|
25
|
+
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
|
26
|
+
type: 'object',
|
|
27
|
+
properties: { age: { type: 'integer', minimum: 0 } },
|
|
28
|
+
required: ['age'],
|
|
29
|
+
};
|
|
30
|
+
describe('validateAgainstSchemas', () => {
|
|
31
|
+
let loader;
|
|
32
|
+
beforeEach(() => {
|
|
33
|
+
resolveJsonDocument.mockReset();
|
|
34
|
+
loader = createSchemaLoader(createInMemoryTtlCache({ ttlMs: 60_000 }));
|
|
35
|
+
});
|
|
36
|
+
function mockSchemaResponses(map) {
|
|
37
|
+
resolveJsonDocument.mockImplementation((async (url) => {
|
|
38
|
+
const schema = map[url];
|
|
39
|
+
if (!schema) {
|
|
40
|
+
throw new ResolverHttpError(url, 404);
|
|
41
|
+
}
|
|
42
|
+
return { json: schema, finalUrl: url };
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
it('returns void when the payload passes every schema', async () => {
|
|
46
|
+
mockSchemaResponses({ [SCHEMA_URL_NAME]: NAME_SCHEMA, [SCHEMA_URL_AGE]: AGE_SCHEMA });
|
|
47
|
+
await expect(validateAgainstSchemas({ name: 'Alice', age: 30 }, [SCHEMA_URL_NAME, SCHEMA_URL_AGE], loader)).resolves.toBeUndefined();
|
|
48
|
+
});
|
|
49
|
+
describe('payload-invalid failures (accumulating)', () => {
|
|
50
|
+
it('throws SchemaPayloadError with a failure per Ajv failure', async () => {
|
|
51
|
+
mockSchemaResponses({ [SCHEMA_URL_NAME]: NAME_SCHEMA });
|
|
52
|
+
const promise = validateAgainstSchemas({ age: 30 }, [SCHEMA_URL_NAME], loader);
|
|
53
|
+
await expect(promise).rejects.toBeInstanceOf(SchemaPayloadError);
|
|
54
|
+
await expect(promise).rejects.toMatchObject({
|
|
55
|
+
failures: expect.arrayContaining([expect.objectContaining({ code: 'schema.payload-invalid' })]),
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
it('accumulates failures across multiple schemas with allErrors', async () => {
|
|
59
|
+
mockSchemaResponses({ [SCHEMA_URL_NAME]: NAME_SCHEMA, [SCHEMA_URL_AGE]: AGE_SCHEMA });
|
|
60
|
+
try {
|
|
61
|
+
await validateAgainstSchemas({}, [SCHEMA_URL_NAME, SCHEMA_URL_AGE], loader);
|
|
62
|
+
throw new Error('expected validateAgainstSchemas to throw');
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
expect(e).toBeInstanceOf(SchemaPayloadError);
|
|
66
|
+
expect(e.failures.length).toBeGreaterThanOrEqual(2);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
it('populates pointer from Ajv instancePath', async () => {
|
|
70
|
+
mockSchemaResponses({ [SCHEMA_URL_AGE]: AGE_SCHEMA });
|
|
71
|
+
try {
|
|
72
|
+
await validateAgainstSchemas({ age: -1 }, [SCHEMA_URL_AGE], loader);
|
|
73
|
+
throw new Error('expected validateAgainstSchemas to throw');
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
expect(e).toBeInstanceOf(SchemaPayloadError);
|
|
77
|
+
expect(e.failures[0].pointer).toBe('/age');
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
it('populates received from Ajv data and expected from Ajv params', async () => {
|
|
81
|
+
mockSchemaResponses({ [SCHEMA_URL_AGE]: AGE_SCHEMA });
|
|
82
|
+
try {
|
|
83
|
+
await validateAgainstSchemas({ age: -1 }, [SCHEMA_URL_AGE], loader);
|
|
84
|
+
throw new Error('expected validateAgainstSchemas to throw');
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
expect(e).toBeInstanceOf(SchemaPayloadError);
|
|
88
|
+
const failure = e.failures[0];
|
|
89
|
+
expect(failure.received).toBe(-1);
|
|
90
|
+
expect(failure.expected).toEqual(expect.objectContaining({ comparison: '>=', limit: 0 }));
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
describe('fetch failures (fail-fast)', () => {
|
|
95
|
+
it('throws SchemaFetchFailedError when a schema cannot be loaded', async () => {
|
|
96
|
+
resolveJsonDocument.mockRejectedValue(new ResolverHttpError(SCHEMA_URL_NAME, 404));
|
|
97
|
+
await expect(validateAgainstSchemas({}, [SCHEMA_URL_NAME], loader)).rejects.toBeInstanceOf(SchemaFetchFailedError);
|
|
98
|
+
});
|
|
99
|
+
it('aborts on the first fetch failure without checking subsequent schemas', async () => {
|
|
100
|
+
mockSchemaResponses({ [SCHEMA_URL_AGE]: AGE_SCHEMA });
|
|
101
|
+
await expect(validateAgainstSchemas({ age: 30 }, [SCHEMA_URL_NAME, SCHEMA_URL_AGE], loader)).rejects.toMatchObject({
|
|
102
|
+
name: 'SchemaFetchFailedError',
|
|
103
|
+
received: SCHEMA_URL_NAME,
|
|
104
|
+
});
|
|
105
|
+
const fetchedUrls = resolveJsonDocument.mock.calls.map((args) => args[0]);
|
|
106
|
+
expect(fetchedUrls).toContain(SCHEMA_URL_NAME);
|
|
107
|
+
expect(fetchedUrls).not.toContain(SCHEMA_URL_AGE);
|
|
108
|
+
});
|
|
109
|
+
it("wraps a custom loader's error as SchemaFetchFailedError", async () => {
|
|
110
|
+
const failingLoad = jest.fn(async () => {
|
|
111
|
+
throw new Error('loader exploded');
|
|
112
|
+
});
|
|
113
|
+
const customLoader = { load: failingLoad };
|
|
114
|
+
const error = await validateAgainstSchemas({}, [SCHEMA_URL_NAME], customLoader).catch((e) => e);
|
|
115
|
+
expect(error).toBeInstanceOf(SchemaFetchFailedError);
|
|
116
|
+
expect(error.cause).toBeInstanceOf(Error);
|
|
117
|
+
});
|
|
118
|
+
it('passes URLs to the supplied loader and validates the returned schema', async () => {
|
|
119
|
+
const load = jest.fn(async () => NAME_SCHEMA);
|
|
120
|
+
const customLoader = { load: load };
|
|
121
|
+
await expect(validateAgainstSchemas({ name: 'Alice' }, [SCHEMA_URL_NAME], customLoader)).resolves.toBeUndefined();
|
|
122
|
+
expect(load).toHaveBeenCalledWith(SCHEMA_URL_NAME);
|
|
123
|
+
expect(resolveJsonDocument).not.toHaveBeenCalled();
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
describe('compilation failures (fail-fast)', () => {
|
|
127
|
+
it('throws SchemaCompilationFailedError when the fetched schema is invalid', async () => {
|
|
128
|
+
mockSchemaResponses({ [SCHEMA_URL_NAME]: { type: 'not-a-real-type' } });
|
|
129
|
+
await expect(validateAgainstSchemas({}, [SCHEMA_URL_NAME], loader)).rejects.toBeInstanceOf(SchemaCompilationFailedError);
|
|
130
|
+
});
|
|
131
|
+
it('throws SchemaCompilationFailedError for an inline schema that does not compile', async () => {
|
|
132
|
+
const badSchema = { type: 'not-a-real-type' };
|
|
133
|
+
await expect(validateAgainstSchemas({}, [badSchema], loader)).rejects.toBeInstanceOf(SchemaCompilationFailedError);
|
|
134
|
+
expect(resolveJsonDocument).not.toHaveBeenCalled();
|
|
135
|
+
});
|
|
136
|
+
it('attributes inline-schema compilation failure to <inline schema>', async () => {
|
|
137
|
+
mockSchemaResponses({ [SCHEMA_URL_NAME]: NAME_SCHEMA });
|
|
138
|
+
const badInline = { type: 'not-a-real-type' };
|
|
139
|
+
const error = (await validateAgainstSchemas({ name: 'Alice' }, [SCHEMA_URL_NAME, badInline], loader).catch((e) => e));
|
|
140
|
+
expect(error).toBeInstanceOf(SchemaCompilationFailedError);
|
|
141
|
+
expect(error.received).toMatch(/.+/);
|
|
142
|
+
expect(error.message).toContain('<inline schema>');
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
describe('inline schema references', () => {
|
|
146
|
+
it('validates against an inline schema without invoking the loader', async () => {
|
|
147
|
+
await expect(validateAgainstSchemas({ name: 'Alice' }, [NAME_SCHEMA], loader)).resolves.toBeUndefined();
|
|
148
|
+
expect(resolveJsonDocument).not.toHaveBeenCalled();
|
|
149
|
+
});
|
|
150
|
+
it('throws SchemaPayloadError from an inline schema when the payload fails', async () => {
|
|
151
|
+
await expect(validateAgainstSchemas({}, [NAME_SCHEMA], loader)).rejects.toBeInstanceOf(SchemaPayloadError);
|
|
152
|
+
expect(resolveJsonDocument).not.toHaveBeenCalled();
|
|
153
|
+
});
|
|
154
|
+
it('mixes URL and inline schema references in a single call', async () => {
|
|
155
|
+
mockSchemaResponses({ [SCHEMA_URL_NAME]: NAME_SCHEMA });
|
|
156
|
+
await expect(validateAgainstSchemas({ name: 'Alice', age: 30 }, [SCHEMA_URL_NAME, AGE_SCHEMA], loader)).resolves.toBeUndefined();
|
|
157
|
+
expect(resolveJsonDocument).toHaveBeenCalledTimes(1);
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
describe('hierarchy', () => {
|
|
161
|
+
it('every concrete error extends SchemaValidationError', async () => {
|
|
162
|
+
resolveJsonDocument.mockRejectedValue(new ResolverHttpError(SCHEMA_URL_NAME, 404));
|
|
163
|
+
await expect(validateAgainstSchemas({}, [SCHEMA_URL_NAME], loader)).rejects.toBeInstanceOf(SchemaValidationError);
|
|
164
|
+
await expect(validateAgainstSchemas({}, [{ type: 'not-a-real-type' }], loader)).rejects.toBeInstanceOf(SchemaValidationError);
|
|
165
|
+
await expect(validateAgainstSchemas({}, [NAME_SCHEMA], loader)).rejects.toBeInstanceOf(SchemaValidationError);
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
//# sourceMappingURL=validate-against-schemas.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-against-schemas.test.js","sourceRoot":"","sources":["../../src/validation/validate-against-schemas.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
import { createInMemoryTtlCache } from '../cache/in-memory-ttl-cache.js';
|
|
3
|
+
const resolveJsonDocument = jest.fn();
|
|
4
|
+
// Mock only the resolver boundary: real jsonld, real document loader, real
|
|
5
|
+
// cache. This pins the seam validateJsonLd's contextCache option promises:
|
|
6
|
+
// the cache outlives the per-call loader instance, so separate validateJsonLd
|
|
7
|
+
// calls sharing one cache resolve each remote @context once.
|
|
8
|
+
jest.unstable_mockModule('../resolvers/index.js', () => ({ resolveJsonDocument }));
|
|
9
|
+
const { validateJsonLd } = await import('./validate-jsonld.js');
|
|
10
|
+
const CONTEXT_URL = 'https://example.com/context';
|
|
11
|
+
const DOCUMENT = { '@context': CONTEXT_URL, name: 'Acme' };
|
|
12
|
+
describe('validateJsonLd contextCache reuse across calls', () => {
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
resolveJsonDocument.mockReset();
|
|
15
|
+
resolveJsonDocument.mockResolvedValue({
|
|
16
|
+
json: { '@context': { name: 'http://schema.org/name' } },
|
|
17
|
+
finalUrl: CONTEXT_URL,
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
it('resolves a remote @context once across separate calls sharing one cache', async () => {
|
|
21
|
+
const contextCache = createInMemoryTtlCache({ ttlMs: 60_000 });
|
|
22
|
+
await expect(validateJsonLd(DOCUMENT, { contextCache })).resolves.toBeUndefined();
|
|
23
|
+
await expect(validateJsonLd(DOCUMENT, { contextCache })).resolves.toBeUndefined();
|
|
24
|
+
expect(resolveJsonDocument).toHaveBeenCalledTimes(1);
|
|
25
|
+
});
|
|
26
|
+
it('re-resolves the remote @context per call without a shared cache', async () => {
|
|
27
|
+
await expect(validateJsonLd(DOCUMENT)).resolves.toBeUndefined();
|
|
28
|
+
await expect(validateJsonLd(DOCUMENT)).resolves.toBeUndefined();
|
|
29
|
+
expect(resolveJsonDocument).toHaveBeenCalledTimes(2);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
//# sourceMappingURL=validate-jsonld.context-cache.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-jsonld.context-cache.test.js","sourceRoot":"","sources":["../../src/validation/validate-jsonld.context-cache.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AACrC,OAAO,EAAE,sBAAsB,EAAE,MAAM,iCAAiC,CAAC;AAGzE,MAAM,mBAAmB,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;AAEtC,2EAA2E;AAC3E,2EAA2E;AAC3E,8EAA8E;AAC9E,6DAA6D;AAC7D,IAAI,CAAC,mBAAmB,CAAC,uBAAuB,EAAE,GAAG,EAAE,CAAC,CAAC,EAAE,mBAAmB,EAAE,CAAC,CAAC,CAAC;AAEnF,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;AAEhE,MAAM,WAAW,GAAG,6BAA6B,CAAC;AAClD,MAAM,QAAQ,GAAG,EAAE,UAAU,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;AAE3D,QAAQ,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC9D,UAAU,CAAC,GAAG,EAAE;QACd,mBAAmB,CAAC,SAAS,EAAE,CAAC;QAChC,mBAAmB,CAAC,iBAAiB,CAAC;YACpC,IAAI,EAAE,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,wBAAwB,EAAE,EAAE;YACxD,QAAQ,EAAE,WAAW;SACb,CAAC,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yEAAyE,EAAE,KAAK,IAAI,EAAE;QACvF,MAAM,YAAY,GAAG,sBAAsB,CAAuB,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;QACrF,MAAM,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;QAClF,MAAM,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;QAClF,MAAM,CAAC,mBAAmB,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;QAChE,MAAM,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;QAChE,MAAM,CAAC,mBAAmB,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { BundledFallbackOptions } from '../bundle/fallback.js';
|
|
2
|
+
import type { TtlCache } from '../cache/ttl-cache.js';
|
|
3
|
+
import type { LoadedRemoteDocument } from '../loaders/jsonld-document-loader.js';
|
|
4
|
+
export interface ValidateJsonLdBuildLoaderOptions extends BundledFallbackOptions {
|
|
5
|
+
/** Whether to use safe mode for JSON-LD expansion. Defaults to true. */
|
|
6
|
+
safe?: boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Optional cache, keyed by URL, for resolved remote `@context` documents.
|
|
9
|
+
* Supplying a shared cache avoids re-fetching the same contexts on every
|
|
10
|
+
* validation (e.g. the credential context on the issuance/verification
|
|
11
|
+
* hot path). Only successful loads are cached, so a URL the SSRF guard
|
|
12
|
+
* rejects is re-checked every time, while a bundled copy served after a
|
|
13
|
+
* host failure is cached like a fetched one. A successfully loaded
|
|
14
|
+
* document is cached even if expansion later rejects it.
|
|
15
|
+
*/
|
|
16
|
+
contextCache?: TtlCache<LoadedRemoteDocument>;
|
|
17
|
+
documentLoader?: never;
|
|
18
|
+
}
|
|
19
|
+
/** A loader for a remote JSON-LD document, as {@link createJsonLdDocumentLoader} returns. */
|
|
20
|
+
export type JsonLdDocumentLoader = (url: string) => Promise<LoadedRemoteDocument>;
|
|
21
|
+
/**
|
|
22
|
+
* Options for {@link validateJsonLd} and {@link expandJsonLd}. Either a
|
|
23
|
+
* `documentLoader` the caller already holds (the same guarded loader shared
|
|
24
|
+
* across several operations, or a test double), or the pieces to build one
|
|
25
|
+
* from; the two are mutually exclusive at the type level, because a cache or
|
|
26
|
+
* fallback option passed beside a ready loader would be silently ignored.
|
|
27
|
+
*/
|
|
28
|
+
export type ValidateJsonLdOptions = ValidateJsonLdBuildLoaderOptions | {
|
|
29
|
+
safe?: boolean;
|
|
30
|
+
documentLoader: JsonLdDocumentLoader;
|
|
31
|
+
contextCache?: never;
|
|
32
|
+
bundledFallback?: never;
|
|
33
|
+
onBundledFallback?: never;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Catches malformed contexts, undefined terms, and structurally invalid
|
|
37
|
+
* linked data by expanding to RDF in safe mode (unless explicitly disabled).
|
|
38
|
+
*
|
|
39
|
+
* Expansion resolves every remote `@context` through a guarded document
|
|
40
|
+
* loader ({@link createJsonLdDocumentLoader}) so `@context` fetches pass the
|
|
41
|
+
* `validatePublicUrl` SSRF guard rather than jsonld.js's default unguarded
|
|
42
|
+
* Node loader. See ADR-033 §7 and issue #707.
|
|
43
|
+
*
|
|
44
|
+
* @see https://github.com/digitalbazaar/jsonld.js#safe-mode jsonld.js safe mode (a jsonld.js option, not a W3C-defined term).
|
|
45
|
+
* @see ../../../../docs/adrs/033-cvc-architecture.md ADR-033 §7 (Security considerations).
|
|
46
|
+
* @throws {JsonLdInvalidShapeError} if `document` is not a non-null object.
|
|
47
|
+
* @throws {JsonLdExpansionFailedError} if `jsonld.toRDF` rejects (which
|
|
48
|
+
* includes a remote `@context` failing the SSRF guard; the guard's
|
|
49
|
+
* rejection is reachable via native `.cause` hops off the thrown error,
|
|
50
|
+
* see {@link rehydrateJsonLdCause}).
|
|
51
|
+
*/
|
|
52
|
+
export declare function validateJsonLd(document: unknown, options?: ValidateJsonLdOptions): Promise<void>;
|
|
53
|
+
/**
|
|
54
|
+
* Expands `document` with the same guarded loader, safe mode and error
|
|
55
|
+
* contract as {@link validateJsonLd}, and returns the expanded form for a
|
|
56
|
+
* caller that displays it (the Playground's context step). Throws the same
|
|
57
|
+
* errors as {@link validateJsonLd}.
|
|
58
|
+
*/
|
|
59
|
+
export declare function expandJsonLd(document: unknown, options?: ValidateJsonLdOptions): Promise<unknown[]>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { JsonLdExpansionFailedError, JsonLdInvalidShapeError } from './errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* jsonld.js's own `JsonLdError` wraps a document loader's rejection in the
|
|
4
|
+
* proprietary `details.cause` and never sets native `Error.cause`
|
|
5
|
+
* (jsonld@8.3.3 `lib/JsonLdError.js`, loader wrap in
|
|
6
|
+
* `lib/ContextResolver.js`), so a plain `.cause` walk over a caught
|
|
7
|
+
* expansion failure dead-ends one hop early: the SSRF guard's rejection
|
|
8
|
+
* (see `createJsonLdDocumentLoader`) is reachable only via the non-standard
|
|
9
|
+
* `error.details.cause`, unlike the schema-fetch path (`schema-loader.ts`),
|
|
10
|
+
* which surfaces the same class of rejection on the native chain.
|
|
11
|
+
*
|
|
12
|
+
* A term's own remote `@context` (a JSON-LD 1.1 "scoped context") loses even
|
|
13
|
+
* that: jsonld.js resolves it through the same loader, but on failure its
|
|
14
|
+
* scoped-context branch (`lib/context.js`) discards the loader's error
|
|
15
|
+
* entirely and throws a fresh `JsonLdError` whose `details` carry no `cause`
|
|
16
|
+
* at all. `fallback` recovers the guard's rejection in that case: `toRDF`'s
|
|
17
|
+
* `documentLoader` is wrapped ({@link validateJsonLd}) to record the last
|
|
18
|
+
* error it threw during the call, and that call-scoped record is passed here
|
|
19
|
+
* as `fallback`. Because jsonld.js resolves `@context` documents one at a
|
|
20
|
+
* time and any loader throw immediately aborts the whole call (no later,
|
|
21
|
+
* unrelated loader call can follow one that failed), a recorded loader error
|
|
22
|
+
* is always the proximate cause of whatever expansion failure this function
|
|
23
|
+
* is asked to rehydrate, whether or not jsonld.js chose to keep its own
|
|
24
|
+
* reference to it.
|
|
25
|
+
*
|
|
26
|
+
* Rehydrates the recovered error (preferring `details.cause` when jsonld.js
|
|
27
|
+
* kept it) onto `error.cause` so a plain `.cause` walk reaches it. Mutates
|
|
28
|
+
* and returns the same object jsonld.js just threw for this call (safe:
|
|
29
|
+
* nothing else holds a reference to it), and only when there is something to
|
|
30
|
+
* recover and jsonld.js has not already set a `cause` itself, so a malformed
|
|
31
|
+
* `@context` that never reached the document loader (and so has nothing
|
|
32
|
+
* buried and no fallback recorded) does not gain a spurious one.
|
|
33
|
+
*
|
|
34
|
+
* @see https://github.com/uncefact/tests-untp/issues/773
|
|
35
|
+
*/
|
|
36
|
+
function rehydrateJsonLdCause(error, fallback) {
|
|
37
|
+
if (!(error instanceof Error) || error.cause !== undefined) {
|
|
38
|
+
return error;
|
|
39
|
+
}
|
|
40
|
+
const buried = error.details?.cause;
|
|
41
|
+
const recovered = buried !== undefined ? buried : fallback;
|
|
42
|
+
if (recovered !== undefined) {
|
|
43
|
+
error.cause = recovered;
|
|
44
|
+
}
|
|
45
|
+
return error;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Loads jsonld and the guarded document loader lazily, runs `operation` with
|
|
49
|
+
* a loader that records its last failure, and rethrows any rejection as a
|
|
50
|
+
* {@link JsonLdExpansionFailedError} whose cause is the loader's typed error
|
|
51
|
+
* where one was buried (see {@link rehydrateJsonLdCause}). Shared by
|
|
52
|
+
* {@link validateJsonLd} and {@link expandJsonLd}.
|
|
53
|
+
*/
|
|
54
|
+
async function withJsonLd(document, options, operation) {
|
|
55
|
+
if (typeof document !== 'object' || document === null) {
|
|
56
|
+
throw new JsonLdInvalidShapeError(document);
|
|
57
|
+
}
|
|
58
|
+
// Dynamic import: jsonld pulls in undici/TextDecoder which aren't
|
|
59
|
+
// available in jsdom, so we load it lazily to allow test mocking. The
|
|
60
|
+
// guarded document loader is loaded lazily for the same reason (it depends
|
|
61
|
+
// on the resolvers/node stack, which uses undici and node:dns).
|
|
62
|
+
const jsonldModule = await import('jsonld');
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
64
|
+
const jsonld = ('default' in jsonldModule ? jsonldModule.default : jsonldModule);
|
|
65
|
+
const documentLoader = options?.documentLoader ??
|
|
66
|
+
(await import('../loaders/jsonld-document-loader.js')).createJsonLdDocumentLoader({
|
|
67
|
+
cache: options?.contextCache,
|
|
68
|
+
bundledFallback: options?.bundledFallback,
|
|
69
|
+
onBundledFallback: options?.onBundledFallback,
|
|
70
|
+
});
|
|
71
|
+
// Recorded for rehydrateJsonLdCause's fallback: the last error the loader
|
|
72
|
+
// threw during this call (undefined if every fetch it made succeeded).
|
|
73
|
+
let lastLoaderError;
|
|
74
|
+
const trackedDocumentLoader = async (url) => {
|
|
75
|
+
try {
|
|
76
|
+
return await documentLoader(url);
|
|
77
|
+
}
|
|
78
|
+
catch (loaderError) {
|
|
79
|
+
lastLoaderError = loaderError;
|
|
80
|
+
throw loaderError;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
// The cast bridges @types/jsonld, which types documentLoader with a legacy
|
|
84
|
+
// callback signature; jsonld.js itself accepts the single-argument
|
|
85
|
+
// promise-returning loader used here.
|
|
86
|
+
const processorOptions = {
|
|
87
|
+
safe: options?.safe ?? true,
|
|
88
|
+
documentLoader: trackedDocumentLoader,
|
|
89
|
+
};
|
|
90
|
+
try {
|
|
91
|
+
return await operation(jsonld, document, processorOptions);
|
|
92
|
+
}
|
|
93
|
+
catch (cause) {
|
|
94
|
+
throw new JsonLdExpansionFailedError(rehydrateJsonLdCause(cause, lastLoaderError));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Catches malformed contexts, undefined terms, and structurally invalid
|
|
99
|
+
* linked data by expanding to RDF in safe mode (unless explicitly disabled).
|
|
100
|
+
*
|
|
101
|
+
* Expansion resolves every remote `@context` through a guarded document
|
|
102
|
+
* loader ({@link createJsonLdDocumentLoader}) so `@context` fetches pass the
|
|
103
|
+
* `validatePublicUrl` SSRF guard rather than jsonld.js's default unguarded
|
|
104
|
+
* Node loader. See ADR-033 §7 and issue #707.
|
|
105
|
+
*
|
|
106
|
+
* @see https://github.com/digitalbazaar/jsonld.js#safe-mode jsonld.js safe mode (a jsonld.js option, not a W3C-defined term).
|
|
107
|
+
* @see ../../../../docs/adrs/033-cvc-architecture.md ADR-033 §7 (Security considerations).
|
|
108
|
+
* @throws {JsonLdInvalidShapeError} if `document` is not a non-null object.
|
|
109
|
+
* @throws {JsonLdExpansionFailedError} if `jsonld.toRDF` rejects (which
|
|
110
|
+
* includes a remote `@context` failing the SSRF guard; the guard's
|
|
111
|
+
* rejection is reachable via native `.cause` hops off the thrown error,
|
|
112
|
+
* see {@link rehydrateJsonLdCause}).
|
|
113
|
+
*/
|
|
114
|
+
export async function validateJsonLd(document, options) {
|
|
115
|
+
await withJsonLd(document, options, (jsonld, doc, processorOptions) => jsonld.toRDF(doc, processorOptions));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Expands `document` with the same guarded loader, safe mode and error
|
|
119
|
+
* contract as {@link validateJsonLd}, and returns the expanded form for a
|
|
120
|
+
* caller that displays it (the Playground's context step). Throws the same
|
|
121
|
+
* errors as {@link validateJsonLd}.
|
|
122
|
+
*/
|
|
123
|
+
export async function expandJsonLd(document, options) {
|
|
124
|
+
return withJsonLd(document, options, (jsonld, doc, processorOptions) => jsonld.expand(doc, processorOptions));
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=validate-jsonld.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-jsonld.js","sourceRoot":"","sources":["../../src/validation/validate-jsonld.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import http from 'node:http';
|
|
2
|
+
import { validateJsonLd } from './validate-jsonld.js';
|
|
3
|
+
import { JsonLdExpansionFailedError } from './errors.js';
|
|
4
|
+
import { UrlValidationError } from '../node/errors.js';
|
|
5
|
+
/**
|
|
6
|
+
* Walks the native `Error.cause` chain from `error`, collecting every link.
|
|
7
|
+
* jsonld.js's own `JsonLdError` buries a document loader's rejection at the
|
|
8
|
+
* non-standard `details.cause` and never sets native `Error.cause`;
|
|
9
|
+
* `validateJsonLd` rehydrates it onto `error.cause` when wrapping an
|
|
10
|
+
* expansion failure (see `validate-jsonld.ts`), so a plain `.cause` walk
|
|
11
|
+
* reaches the SSRF guard's rejection the same way it does on the
|
|
12
|
+
* schema-fetch path (`schema-loader.ssrf.test.ts`).
|
|
13
|
+
*/
|
|
14
|
+
function nativeCauseChain(error) {
|
|
15
|
+
const chain = [];
|
|
16
|
+
let current = error;
|
|
17
|
+
while (current instanceof Error && current.cause !== undefined) {
|
|
18
|
+
chain.push(current.cause);
|
|
19
|
+
current = current.cause;
|
|
20
|
+
}
|
|
21
|
+
return chain;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* End-to-end SSRF regression for JSON-LD expansion. Unlike `validate-jsonld.test.ts`,
|
|
25
|
+
* this suite does NOT mock `jsonld` or the resolver stack: it exercises the real
|
|
26
|
+
* `jsonld.toRDF` + guarded document loader against a live loopback server standing
|
|
27
|
+
* in for an internal service.
|
|
28
|
+
*
|
|
29
|
+
* The load-bearing assertion is that the canary server is NEVER contacted. A prior
|
|
30
|
+
* (unguarded) implementation would let jsonld.js's default Node loader fetch a
|
|
31
|
+
* private `@context` URL, hitting the canary. The guard rejects the URL before any
|
|
32
|
+
* connection, so `hits` stays 0 regardless of how the expansion ultimately fails.
|
|
33
|
+
*
|
|
34
|
+
* @see https://github.com/uncefact/tests-untp/issues/707
|
|
35
|
+
*/
|
|
36
|
+
describe('validateJsonLd SSRF guard (real jsonld + guarded loader)', () => {
|
|
37
|
+
let server;
|
|
38
|
+
let baseUrl;
|
|
39
|
+
let hits = 0;
|
|
40
|
+
beforeAll(async () => {
|
|
41
|
+
server = http.createServer((_req, res) => {
|
|
42
|
+
hits += 1;
|
|
43
|
+
res.setHeader('content-type', 'application/ld+json');
|
|
44
|
+
res.end(JSON.stringify({ '@context': {} }));
|
|
45
|
+
});
|
|
46
|
+
await new Promise((resolve) => server.listen(0, '127.0.0.1', resolve));
|
|
47
|
+
const address = server.address();
|
|
48
|
+
baseUrl = `http://127.0.0.1:${address.port}`;
|
|
49
|
+
});
|
|
50
|
+
afterAll(async () => {
|
|
51
|
+
await new Promise((resolve, reject) => server.close((err) => (err ? reject(err) : resolve())));
|
|
52
|
+
});
|
|
53
|
+
beforeEach(() => {
|
|
54
|
+
hits = 0;
|
|
55
|
+
});
|
|
56
|
+
it('never contacts a private @context URL during expansion', async () => {
|
|
57
|
+
const malicious = { '@context': `${baseUrl}/internal-context`, name: 'value' };
|
|
58
|
+
const error = await validateJsonLd(malicious).catch((e) => e);
|
|
59
|
+
expect(error).toBeInstanceOf(JsonLdExpansionFailedError);
|
|
60
|
+
// The rejection came from the SSRF guard, not an unrelated expansion failure.
|
|
61
|
+
expect(nativeCauseChain(error)).toContainEqual(expect.any(UrlValidationError));
|
|
62
|
+
// The security property: the loopback canary was never reached.
|
|
63
|
+
expect(hits).toBe(0);
|
|
64
|
+
});
|
|
65
|
+
it('rejects a metadata-service @context literal before any connection', async () => {
|
|
66
|
+
const malicious = { '@context': 'http://169.254.169.254/latest/meta-data', name: 'value' };
|
|
67
|
+
const error = await validateJsonLd(malicious).catch((e) => e);
|
|
68
|
+
expect(error).toBeInstanceOf(JsonLdExpansionFailedError);
|
|
69
|
+
expect(nativeCauseChain(error)).toContainEqual(expect.any(UrlValidationError));
|
|
70
|
+
});
|
|
71
|
+
it('expands a document with an inline @context without any network fetch', async () => {
|
|
72
|
+
const document = { '@context': { name: 'http://schema.org/name' }, name: 'Acme' };
|
|
73
|
+
await expect(validateJsonLd(document)).resolves.toBeUndefined();
|
|
74
|
+
expect(hits).toBe(0);
|
|
75
|
+
});
|
|
76
|
+
it('reaches the UrlValidationError by plain .cause hops, matching the schema-fetch path shape', async () => {
|
|
77
|
+
const malicious = { '@context': `${baseUrl}/internal-context`, name: 'value' };
|
|
78
|
+
const error = (await validateJsonLd(malicious).catch((e) => e));
|
|
79
|
+
// First hop: jsonld.js's own JsonLdError (still carries `.details` for
|
|
80
|
+
// callers that want the raw jsonld diagnostic).
|
|
81
|
+
expect(error.cause).toBeInstanceOf(Error);
|
|
82
|
+
expect(error.cause.name).toBe('jsonld.InvalidUrl');
|
|
83
|
+
// Second hop: the guard's rejection itself, reachable without digging
|
|
84
|
+
// into jsonld.js's proprietary `details.cause`.
|
|
85
|
+
expect(error.cause.cause).toBeInstanceOf(UrlValidationError);
|
|
86
|
+
});
|
|
87
|
+
it('never contacts a private scoped (term-level) @context URL during expansion', async () => {
|
|
88
|
+
// A term can declare its own remote @context (JSON-LD 1.1 scoped contexts).
|
|
89
|
+
// jsonld.js resolves it through the same guarded loader as a top-level
|
|
90
|
+
// @context, so it must be rejected the same way.
|
|
91
|
+
const malicious = {
|
|
92
|
+
'@context': { myTerm: { '@id': 'http://schema.org/myTerm', '@context': `${baseUrl}/internal-scoped-context` } },
|
|
93
|
+
myTerm: 'value',
|
|
94
|
+
};
|
|
95
|
+
const error = await validateJsonLd(malicious).catch((e) => e);
|
|
96
|
+
expect(error).toBeInstanceOf(JsonLdExpansionFailedError);
|
|
97
|
+
// The rejection came from the SSRF guard, reachable by plain .cause hops.
|
|
98
|
+
// jsonld.js's scoped-context branch (lib/context.js) discards the loader
|
|
99
|
+
// failure entirely and throws a fresh 'invalid scoped context' JsonLdError
|
|
100
|
+
// with no `details.cause` of its own, so this can only pass if
|
|
101
|
+
// validateJsonLd recovers the guard's rejection some other way (tracking
|
|
102
|
+
// it at the document-loader call, see `validate-jsonld.ts`).
|
|
103
|
+
expect(nativeCauseChain(error)).toContainEqual(expect.any(UrlValidationError));
|
|
104
|
+
// The security property: the loopback canary was never reached.
|
|
105
|
+
expect(hits).toBe(0);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
//# sourceMappingURL=validate-jsonld.ssrf.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-jsonld.ssrf.test.js","sourceRoot":"","sources":["../../src/validation/validate-jsonld.ssrf.test.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;;;;;;;GAQG;AACH,SAAS,gBAAgB,CAAC,KAAc;IACtC,MAAM,KAAK,GAAc,EAAE,CAAC;IAC5B,IAAI,OAAO,GAAY,KAAK,CAAC;IAC7B,OAAO,OAAO,YAAY,KAAK,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QAC/D,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC1B,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC;IAC1B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,QAAQ,CAAC,0DAA0D,EAAE,GAAG,EAAE;IACxE,IAAI,MAAmB,CAAC;IACxB,IAAI,OAAe,CAAC;IACpB,IAAI,IAAI,GAAG,CAAC,CAAC;IAEb,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,EAAE,GAAG,EAAE,EAAE;YACvC,IAAI,IAAI,CAAC,CAAC;YACV,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,qBAAqB,CAAC,CAAC;YACrD,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QACH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;QAC7E,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAiB,CAAC;QAChD,OAAO,GAAG,oBAAoB,OAAO,CAAC,IAAI,EAAE,CAAC;IAC/C,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,KAAK,IAAI,EAAE;QAClB,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;IACvG,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,GAAG,EAAE;QACd,IAAI,GAAG,CAAC,CAAC;IACX,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,SAAS,GAAG,EAAE,UAAU,EAAE,GAAG,OAAO,mBAAmB,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAE/E,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QACvE,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,0BAA0B,CAAC,CAAC;QACzD,8EAA8E;QAC9E,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC;QAC/E,gEAAgE;QAChE,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,SAAS,GAAG,EAAE,UAAU,EAAE,yCAAyC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAE3F,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QACvE,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,0BAA0B,CAAC,CAAC;QACzD,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC;IACjF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;QACpF,MAAM,QAAQ,GAAG,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,wBAAwB,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;QAElF,MAAM,MAAM,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAC;QAChE,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2FAA2F,EAAE,KAAK,IAAI,EAAE;QACzG,MAAM,SAAS,GAAG,EAAE,UAAU,EAAE,GAAG,OAAO,mBAAmB,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAE/E,MAAM,KAAK,GAAG,CAAC,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,CAAC,CAA+B,CAAC;QAEvG,uEAAuE;QACvE,gDAAgD;QAChD,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC1C,MAAM,CAAE,KAAK,CAAC,KAAe,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QAC9D,sEAAsE;QACtE,gDAAgD;QAChD,MAAM,CAAE,KAAK,CAAC,KAAe,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4EAA4E,EAAE,KAAK,IAAI,EAAE;QAC1F,4EAA4E;QAC5E,uEAAuE;QACvE,iDAAiD;QACjD,MAAM,SAAS,GAAG;YAChB,UAAU,EAAE,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,0BAA0B,EAAE,UAAU,EAAE,GAAG,OAAO,0BAA0B,EAAE,EAAE;YAC/G,MAAM,EAAE,OAAO;SAChB,CAAC;QAEF,MAAM,KAAK,GAAG,MAAM,cAAc,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC;QACvE,MAAM,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,0BAA0B,CAAC,CAAC;QACzD,0EAA0E;QAC1E,yEAAyE;QACzE,2EAA2E;QAC3E,+DAA+D;QAC/D,yEAAyE;QACzE,6DAA6D;QAC7D,MAAM,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,CAAC;QAC/E,gEAAgE;QAChE,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|