Skip to content

Commit 2b37d29

Browse files
committed
Emit error on unsupported versions
1 parent e81f447 commit 2b37d29

File tree

3 files changed

+100
-0
lines changed

3 files changed

+100
-0
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ fs.createReadStream('myfile.rdf')
6161
.on('end', () => console.log('All triples were parsed!'));
6262
```
6363

64+
The error thrown for unsupported versions can be skipped
65+
by setting `parseUnsupportedVersions` to `true` when constructing the parser.
66+
6467
### Manually write strings to the parser
6568

6669
```javascript
@@ -110,6 +113,8 @@ Optionally, the following parameters can be set in the `RdfXmlParser` constructo
110113
* `allowDuplicateRdfIds`: By default [multiple occurrences of the same `rdf:ID` value are not allowed](https://www.w3.org/TR/rdf-syntax-grammar/#section-Syntax-ID-xml-base). By setting this option to `true`, this uniqueness check can be disabled. _(Default: `false`)_
111114
* `validateUri`: By default, the parser validates each URI. _(Default: `true`)_
112115
* `iriValidationStrategy`: Allows to customize the used IRI validation strategy using the `IriValidationStrategy` enumeration. IRI validation is handled by [validate-iri.js](https://github.com/comunica/validate-iri.js/). _(Default: `IriValidationStrategy.Pragmatic`)_
116+
* `parseUnsupportedVersions`: If no error should be emitted on unsupported versions. _(Default: `false`)_
117+
* `version`: The version that was supplied as a media type parameter. _(Default: `undefined`)_
113118

114119
```javascript
115120
new RdfXmlParser({
@@ -120,6 +125,7 @@ new RdfXmlParser({
120125
trackPosition: true,
121126
allowDuplicateRdfIds: true,
122127
validateUri: true,
128+
parseUnsupportedVersions: false,
123129
});
124130
```
125131

lib/RdfXmlParser.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ export class RdfXmlParser extends Transform implements RDF.Sink<EventEmitter, RD
3939
];
4040
// tslint:disable-next-line:max-line-length
4141
public static readonly NCNAME_MATCHER = /^([A-Za-z\xC0-\xD6\xD8-\xF6\u{F8}-\u{2FF}\u{370}-\u{37D}\u{37F}-\u{1FFF}\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFFD}\u{10000}-\u{EFFFF}_])([A-Za-z\xC0-\xD6\xD8-\xF6\u{F8}-\u{2FF}\u{370}-\u{37D}\u{37F}-\u{1FFF}\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}\u{FDF0}-\u{FFFD}\u{10000}-\u{EFFFF}_\-.0-9#xB7\u{0300}-\u{036F}\u{203F}-\u{2040}])*$/u;
42+
public static SUPPORTED_VERSIONS: string[] = [
43+
'1.2',
44+
'1.2-basic',
45+
'1.1',
46+
];
4247

4348
public readonly trackPosition?: boolean;
4449

@@ -50,6 +55,8 @@ export class RdfXmlParser extends Transform implements RDF.Sink<EventEmitter, RD
5055
private readonly saxParser: SaxesParser;
5156
private readonly validateUri: boolean;
5257
private readonly iriValidationStrategy: IriValidationStrategy;
58+
private readonly parseUnsupportedVersions: boolean;
59+
private version: string | undefined;
5360

5461
private readonly activeTagStack: IActiveTag[] = [];
5562
private readonly nodeIds: {[id: string]: boolean} = {};
@@ -76,6 +83,8 @@ export class RdfXmlParser extends Transform implements RDF.Sink<EventEmitter, RD
7683
if (!this.iriValidationStrategy) {
7784
this.iriValidationStrategy = this.validateUri ? IriValidationStrategy.Pragmatic : IriValidationStrategy.None;
7885
}
86+
this.parseUnsupportedVersions = !!args?.parseUnsupportedVersions;
87+
this.version = args?.version;
7988

8089
this.saxParser = new SaxesParser({ xmlns: true, position: this.trackPosition });
8190

@@ -97,6 +106,14 @@ export class RdfXmlParser extends Transform implements RDF.Sink<EventEmitter, RD
97106
}
98107

99108
public _transform(chunk: any, encoding: BufferEncoding, callback: (error?: Error | null, data?: any) => void) {
109+
if (this.version) {
110+
const version = this.version;
111+
this.version = undefined;
112+
if (!this.isValidVersion(version)) {
113+
return callback(this.newParseError(`Detected unsupported version as media type parameter: ${version}`));
114+
}
115+
}
116+
100117
try {
101118
this.saxParser.write(chunk);
102119
} catch (e) {
@@ -165,6 +182,14 @@ export class RdfXmlParser extends Transform implements RDF.Sink<EventEmitter, RD
165182
return this.dataFactory.literal(value, activeTag.datatype ? activeTag.datatype : activeTag.language ? { language: activeTag.language, direction: activeTag.rdfVersion ? activeTag.direction : undefined } : undefined)
166183
}
167184

185+
/**
186+
* If the given version is valid for this parser to handle.
187+
* @param version A version string.
188+
*/
189+
public isValidVersion(version: string): boolean {
190+
return this.parseUnsupportedVersions || RdfXmlParser.SUPPORTED_VERSIONS.includes(version);
191+
}
192+
168193
protected attachSaxListeners() {
169194
this.saxParser.on('error', (error) => this.emit('error', error));
170195
this.saxParser.on('opentag', this.onTag.bind(this));
@@ -761,6 +786,9 @@ while ${attribute.value} and ${activeSubjectValue} where found.`);
761786
private setVersion(activeTag: IActiveTag, version: string) {
762787
activeTag.rdfVersion = version;
763788
this.emit('version', version);
789+
if (!this.isValidVersion(version)) {
790+
throw this.newParseError(`Detected unsupported version: ${version}`);
791+
}
764792
}
765793
}
766794

@@ -796,6 +824,14 @@ export interface IRdfXmlParserArgs {
796824
* By default, the "pragmatic" strategy is used.
797825
*/
798826
iriValidationStrategy?: IriValidationStrategy;
827+
/**
828+
* If no error should be emitted on unsupported versions.
829+
*/
830+
parseUnsupportedVersions?: boolean;
831+
/**
832+
* The version that was supplied as a media type parameter.
833+
*/
834+
version?: string;
799835
}
800836

801837
export interface IActiveTag {

test/RdfXmlParser-test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2625,6 +2625,64 @@ abc`)).rejects.toBeTruthy();
26252625
return expect(cb).toHaveBeenCalledWith('1.2');
26262626
});
26272627

2628+
it('throws on an unsupported rdf:version', async () => {
2629+
await expect(parse(parser, `<?xml version="1.0"?>
2630+
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
2631+
xmlns:dc="http://purl.org/dc/elements/1.1/"
2632+
xmlns:ex="http://example.org/stuff/1.0/"
2633+
rdf:version="1.2-unknown">
2634+
<rdf:Description>
2635+
<ex:editor>
2636+
<rdf:Description></rdf:Description>
2637+
</ex:editor>
2638+
</rdf:Description>
2639+
</rdf:RDF>`)).rejects.toThrow(`Detected unsupported version: 1.2-unknown`);
2640+
});
2641+
2642+
it('handles an unsupported rdf:version when parseUnsupportedVersions is true', async () => {
2643+
parser = new RdfXmlParser({ parseUnsupportedVersions: true });
2644+
await expect(parse(parser, `<?xml version="1.0"?>
2645+
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
2646+
xmlns:dc="http://purl.org/dc/elements/1.1/"
2647+
xmlns:ex="http://example.org/stuff/1.0/"
2648+
rdf:version="1.2-unknown">
2649+
<rdf:Description>
2650+
<ex:editor>
2651+
<rdf:Description></rdf:Description>
2652+
</ex:editor>
2653+
</rdf:Description>
2654+
</rdf:RDF>`)).resolves.toHaveLength(1);
2655+
});
2656+
2657+
it('throws on an unsupported version media type parameter', async () => {
2658+
parser = new RdfXmlParser({ version: '1.2-unknown' });
2659+
await expect(parse(parser, `<?xml version="1.0"?>
2660+
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
2661+
xmlns:dc="http://purl.org/dc/elements/1.1/"
2662+
xmlns:ex="http://example.org/stuff/1.0/">
2663+
<rdf:Description>
2664+
<ex:editor>
2665+
<rdf:Description></rdf:Description>
2666+
</ex:editor>
2667+
</rdf:Description>
2668+
</rdf:RDF>`)).rejects.toThrow(`Detected unsupported version as media type parameter: 1.2-unknown`);
2669+
});
2670+
2671+
it('handles an unsupported media type parameter when parseUnsupportedVersions is true', async () => {
2672+
parser = new RdfXmlParser({ parseUnsupportedVersions: true, version: '1.2-unknown' });
2673+
await expect(parse(parser, `<?xml version="1.0"?>
2674+
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
2675+
xmlns:dc="http://purl.org/dc/elements/1.1/"
2676+
xmlns:ex="http://example.org/stuff/1.0/"
2677+
rdf:version="1.2-unknown">
2678+
<rdf:Description>
2679+
<ex:editor>
2680+
<rdf:Description></rdf:Description>
2681+
</ex:editor>
2682+
</rdf:Description>
2683+
</rdf:RDF>`)).resolves.toHaveLength(1);
2684+
});
2685+
26282686
// 2.19
26292687
it('on property elements with rdf:parseType="Triple"', async () => {
26302688
const array = await parse(parser, `<?xml version="1.0"?>

0 commit comments

Comments
 (0)