Skip to content

Commit 7ece660

Browse files
authored
impove detection of XML's anyURI violations (#179)
fixes #178 Signed-off-by: Jan Kowalleck <[email protected]>
1 parent ed457d8 commit 7ece660

File tree

3 files changed

+85
-5
lines changed

3 files changed

+85
-5
lines changed

HISTORY.md

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44

55
## unreleased
66

7+
* Fixed
8+
* Improved omission of invalid `anyURI` when it comes to XML-normalization. ([#178] via [#179])
9+
10+
[#178]: https://github.com/CycloneDX/cyclonedx-javascript-library/issues/178
11+
[#179]: https://github.com/CycloneDX/cyclonedx-javascript-library/pull/179
12+
713
## 1.3.2 - 2022-08-15
814

915
* Fixed

src/serialize/xml/types.ts

+36-5
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Copyright (c) OWASP Foundation. All Rights Reserved.
2020
// eslint-disable-next-line @typescript-eslint/no-namespace
2121
export namespace XmlSchema {
2222

23+
const _anyUriSchemePattern = /^[a-z][a-z0-9+\-.]*$/i
24+
2325
/**
2426
* @see isAnyURI
2527
*/
@@ -28,13 +30,42 @@ export namespace XmlSchema {
2830
* Test whether format is XML::anyURI - best-effort.
2931
*
3032
* @see {@link http://www.w3.org/TR/xmlschema-2/#anyURI}
31-
* @see {@link http://www.datypic.com/sc/xsd/t-xsd_anyURI.html}
33+
* @see {@link https://www.w3.org/2011/04/XMLSchema/TypeLibrary-URI-RFC3986.xsd}
34+
* @see {@link https://www.w3.org/2011/04/XMLSchema/TypeLibrary-IRI-RFC3987.xsd}
3235
*/
3336
export function isAnyURI (value: AnyURI | any): value is AnyURI {
34-
return typeof value === 'string' &&
35-
value.length > 0 &&
36-
Array.from(value).filter(c => c === '#').length <= 1
37-
// TODO add more validation according to spec
37+
if (typeof value !== 'string') {
38+
// not a string
39+
return false
40+
}
41+
if (value.length === 0) {
42+
// empty string
43+
return false
44+
}
45+
46+
const fragmentPos = value.indexOf('#')
47+
let beforeFragment: string
48+
if (fragmentPos >= 0) {
49+
if (value.includes('#', fragmentPos + 1)) {
50+
// has a second fragment marker
51+
return false
52+
}
53+
beforeFragment = value.slice(undefined, fragmentPos)
54+
} else {
55+
beforeFragment = value
56+
}
57+
58+
const schemePos = beforeFragment.indexOf(':')
59+
if (schemePos >= 0) {
60+
if (!_anyUriSchemePattern.test(beforeFragment.slice(undefined, schemePos))) {
61+
// invalid schema
62+
return false
63+
}
64+
}
65+
66+
// @TODO add more validation according to spec
67+
68+
return true
3869
}
3970

4071
}

tests/integration/Serialize.XmlNormalize.test.js

+43
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const { createComplexStructure } = require('../_data/models')
2525
const { loadNormalizeResult, writeNormalizeResult } = require('../_data/normalize')
2626

2727
const {
28+
Models, Enums,
2829
Serialize: {
2930
XML: { Normalize: { Factory: XmlNormalizeFactory } }
3031
},
@@ -73,4 +74,46 @@ describe('Serialize.XmlNormalize', function () {
7374

7475
// TODO add more tests
7576
}))
77+
78+
describe('ExternalReference\'s `anyURI`', () => {
79+
const normalizer = new XmlNormalizeFactory(Spec1dot4).makeForExternalReference()
80+
81+
describe('omit invalid', () => {
82+
[
83+
// only one fragment allowed
84+
'foo#bar#baz',
85+
// scheme must follow the RFC
86+
'[email protected]:peterolson/BigInteger.js.git',
87+
'git%40github.com:peterolson/BigInteger.js.git',
88+
':foo-bar'
89+
].forEach(uri => it(`${uri}`, () => {
90+
const ref = new Models.ExternalReference(uri, Enums.ExternalReferenceType.Other)
91+
const normalized = normalizer.normalize(ref, {}, 'ref')
92+
assert.strictEqual(normalized, undefined)
93+
}))
94+
})
95+
describe('render valid', () => {
96+
[
97+
'https://github.com/peterolson/BigInteger.js.git',
98+
'git+ssh:[email protected]:peterolson/BigInteger.js.git',
99+
'example.com:8080/foo/bar',
100+
'foo#bar',
101+
102+
'g#[email protected]:peterolson/BigInteger.js.git'
103+
].forEach(uri => it(`${uri}`, () => {
104+
const ref = new Models.ExternalReference(uri, Enums.ExternalReferenceType.Other)
105+
const normalized = normalizer.normalize(ref, {}, 'ref')
106+
assert.deepStrictEqual(normalized, {
107+
type: 'element',
108+
name: 'ref',
109+
attributes: { type: 'other' },
110+
children: [{
111+
type: 'element',
112+
name: 'url',
113+
children: uri
114+
}]
115+
})
116+
}))
117+
})
118+
})
76119
})

0 commit comments

Comments
 (0)