Skip to content

Commit

Permalink
fix(bueno): use URL constructor to parse url (#4644)
Browse files Browse the repository at this point in the history
The previous URL validation disallowed localhost and 127.0.0.1-type
endpoints. Instead of using a regex to parse the URL, defer the parsing
to the URL constructor, which is more flexible.

To do so, I updated the TypeScript configuration to allow the use of
DOM-related types in the project.

This change came up as I was attempting to use `proxyBaseUrl` in
headless to call a locally-running instance of a service, which was
exposed on localhost. See
https://coveo.slack.com/archives/G016TA2G485/p1730933467640229
  • Loading branch information
Spuffynism authored Nov 7, 2024
1 parent 61ff4ea commit e48646b
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 17 deletions.
37 changes: 25 additions & 12 deletions packages/bueno/src/values/string-value.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,31 @@ describe('string value', () => {
expect(value.validate('')).toBeNull();
});

it(`when url is true
when passing an invalid URL value
it returns an error description`, () => {
value = new StringValue({url: true});
expect(value.validate('hello')).not.toBeNull();
});

it(`when url is true
when passing a valid URL value
it returns null`, () => {
value = new StringValue({url: true});
expect(value.validate('https://www.coveo.com?test=allo')).toBeNull();
describe('when url is true', () => {
it.each(['hello', 'localhost', '127.0.0.1', '/relative/url'])(
`when passing an invalid URL value of '%s'
it returns an error description`,
(url) => {
value = new StringValue({url: true});
expect(value.validate(url)).not.toBeNull();
}
);

it.each([
'https://www.coveo.com?test=allo',
'https://www.example.org',
'http://www.example.org',
'http://localhost',
'http://127.0.0.1',
'localhost:1717',
])(
`when passing a valid URL value of '%s'
it returns null`,
(url) => {
value = new StringValue({url: true});
expect(value.validate(url)).toBeNull();
}
);
});

it(`when constraining values are specified,
Expand Down
10 changes: 6 additions & 4 deletions packages/bueno/src/values/string-value.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ interface StringValueConfig<T extends string> extends ValueConfig<T> {
}

// Source: https://github.com/jquery-validation/jquery-validation/blob/c1db10a34c0847c28a5bd30e3ee1117e137ca834/src/core.js#L1349
const urlRegex =
/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i;
const ISODateStringRegex =
/^\d{4}(-\d\d(-\d\d(T\d\d:\d\d(:\d\d)?(\.\d+)?(([+-]\d\d:\d\d)|Z)?)?)?)?$/i;

Expand Down Expand Up @@ -48,8 +46,12 @@ export class StringValue<T extends string = string>
return 'value is an empty string.';
}

if (url && !urlRegex.test(value)) {
return 'value is not a valid URL.';
if (url) {
try {
new URL(value);
} catch (e) {
return 'value is not a valid URL.';
}
}

if (regex && !regex.test(value)) {
Expand Down
2 changes: 1 addition & 1 deletion packages/bueno/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"lib": ["ES2023"],
"lib": ["DOM", "ES2023"],
"module": "node16",
"target": "ES2022",
"declaration": false,
Expand Down

0 comments on commit e48646b

Please sign in to comment.