Skip to content

Commit 18f9f97

Browse files
committed
Emit warning on potentially invalid IRIs
1 parent 4c475b0 commit 18f9f97

File tree

6 files changed

+61
-3
lines changed

6 files changed

+61
-3
lines changed

lib/CompileUtil.ts

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export async function compileConfig(
3333
importPaths: state.importPaths,
3434
ignoreImports: false,
3535
absolutizeRelativePaths: true,
36+
logger: loader.logger,
3637
});
3738

3839
// Serialize the config

lib/Loader.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ export class Loader {
2424
private readonly absolutizeRelativePaths: boolean;
2525
private readonly mainModulePath?: string;
2626
private readonly dumpErrorState: boolean;
27-
private readonly logger?: Logger;
27+
public readonly logger?: Logger;
2828
public readonly objectLoader: RdfObjectLoader;
2929

3030
public componentResources: Record<string, Resource> = {};
@@ -241,6 +241,7 @@ export class Loader {
241241
importPaths: state.importPaths,
242242
ignoreImports: false,
243243
absolutizeRelativePaths: this.absolutizeRelativePaths,
244+
logger: this.logger,
244245
}));
245246
} catch (error: unknown) {
246247
throw this.generateErrorLog(error);
@@ -522,6 +523,7 @@ export class Loader {
522523
importPaths: state.importPaths,
523524
ignoreImports: false,
524525
absolutizeRelativePaths: this.absolutizeRelativePaths,
526+
logger: this.logger,
525527
}));
526528
} catch (error: unknown) {
527529
throw this.generateErrorLog(error);
@@ -553,6 +555,7 @@ export class Loader {
553555
importPaths: state.importPaths,
554556
ignoreImports: false,
555557
absolutizeRelativePaths: this.absolutizeRelativePaths,
558+
logger: this.logger,
556559
}), settings);
557560
} catch (error: unknown) {
558561
throw this.generateErrorLog(error);

lib/Util.ts

+8
Original file line numberDiff line numberDiff line change
@@ -266,3 +266,11 @@ export function addFilePathToError(error: Error, filePath: string, fromPath?: st
266266
export function uriToVariableName(uri: string): string {
267267
return uri.replace(/[#./:@\\^-]/gu, '_');
268268
}
269+
270+
/**
271+
* Check if the given IRI is valid.
272+
* @param iri A potential IRI.
273+
*/
274+
export function isValidIri(iri: string): boolean {
275+
return Boolean(/:((\/\/)|(.*:))/u.exec(iri));
276+
}

lib/rdf/RdfParser.ts

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { Readable } from 'stream';
33
import type * as RDF from 'rdf-js';
44
import type { ParseOptions } from 'rdf-parse';
55
import rdfParser from 'rdf-parse';
6+
import type { Logger } from 'winston';
67
import Util = require('../Util');
78
import { PrefetchedDocumentLoader } from './PrefetchedDocumentLoader';
89
import { RdfStreamIncluder } from './RdfStreamIncluder';
@@ -65,4 +66,8 @@ export type RdfParserOptions = ParseOptions & {
6566
* If relative paths 'file://' should be made absolute 'file:///'.
6667
*/
6768
absolutizeRelativePaths?: boolean;
69+
/**
70+
* An optional logger.
71+
*/
72+
logger?: Logger;
6873
};

lib/rdf/RdfStreamIncluder.ts

+15-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ import type { Readable } from 'stream';
33
import { PassThrough } from 'stream';
44
import { DataFactory } from 'rdf-data-factory';
55
import type * as RDF from 'rdf-js';
6-
import { mapTerms } from 'rdf-terms';
6+
import { getNamedNodes, getTerms, mapTerms } from 'rdf-terms';
77
import Util = require('../Util');
8+
import { isValidIri } from '../Util';
89
import { RdfParser } from './RdfParser';
910
import type { RdfParserOptions } from './RdfParser';
1011

@@ -27,6 +28,7 @@ export class RdfStreamIncluder extends PassThrough {
2728

2829
public push(quad: RDF.Quad | null, encoding?: string): boolean {
2930
if (quad) {
31+
// Check for import link
3032
if (!this.parserOptions.ignoreImports && quad.predicate.value === `${Util.PREFIXES.owl}imports`) {
3133
this.runningImporters++;
3234
let relativeFilePath = quad.object.value;
@@ -51,9 +53,21 @@ export class RdfStreamIncluder extends PassThrough {
5153
}).catch((error: any) => this
5254
.emit('error', Util.addFilePathToError(error, relativeFilePath, this.parserOptions.baseIRI)));
5355
}
56+
57+
// Absolutize relative file paths
5458
if (this.parserOptions.absolutizeRelativePaths) {
5559
quad = mapTerms(quad, term => this._absolutize(term));
5660
}
61+
62+
// Validate IRIs
63+
if (this.parserOptions.logger) {
64+
for (const term of getNamedNodes(getTerms(quad))) {
65+
if (!isValidIri(term.value)) {
66+
this.parserOptions.logger.warn(`Detected potentially invalid IRI: '${term.value}'`);
67+
}
68+
}
69+
}
70+
5771
return super.push(quad);
5872
}
5973
if (!--this.runningImporters) {

test/Util-test.ts

+28-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('Util', () => {
88
objectLoader = new Loader().objectLoader;
99
});
1010

11-
describe('#captureType', () => {
11+
describe('captureType', () => {
1212
it('should capture strings', () => {
1313
expect((<any> Util.captureType(objectLoader.createCompactedResource('"aaa"'), objectLoader.createCompactedResource({ range: `${Util.PREFIXES.xsd}string` }), objectLoader)).term.valueRaw)
1414
.toBeUndefined();
@@ -89,4 +89,31 @@ describe('Util', () => {
8989
.toEqual(256.36);
9090
});
9191
});
92+
93+
describe('isValidIri', () => {
94+
it('should be false on an empty string', () => {
95+
expect(Util.isValidIri('')).toBeFalsy();
96+
});
97+
98+
it('should be false without colon', () => {
99+
expect(Util.isValidIri('abc')).toBeFalsy();
100+
});
101+
102+
it('should be false with one colon without //', () => {
103+
expect(Util.isValidIri('a:a')).toBeFalsy();
104+
});
105+
106+
it('should be false with one colon with further //', () => {
107+
expect(Util.isValidIri('a:a//a')).toBeFalsy();
108+
});
109+
110+
it('should be true with one colon followed by immediate //', () => {
111+
expect(Util.isValidIri('a://a')).toBeTruthy();
112+
});
113+
114+
it('should be true with more than one colon', () => {
115+
expect(Util.isValidIri('a:a:a')).toBeTruthy();
116+
expect(Util.isValidIri('a:a:a:a')).toBeTruthy();
117+
});
118+
});
92119
});

0 commit comments

Comments
 (0)