5 |
5 |
* Redistribution terms are gathered in the `copyright' file.
|
6 |
6 |
*/
|
7 |
7 |
|
|
8 |
const MAX_URL_PATH_LEN = 12;
|
|
9 |
const MAX_URL_PATH_CHARS = 255;
|
|
10 |
const MAX_DOMAIN_LEN = 7;
|
|
11 |
const MAX_DOMAIN_CHARS = 100;
|
|
12 |
|
8 |
13 |
const proto_regex = /^(\w+):\/\/(.*)$/;
|
9 |
14 |
|
10 |
15 |
const user_re = "[^/?#@]+@"
|
... | ... | |
37 |
42 |
[deco.domain, deco.path, deco.query] = http_match.slice(1, 4);
|
38 |
43 |
}
|
39 |
44 |
|
40 |
|
if (deco.domain)
|
41 |
|
deco.domain = deco.domain.split(".");
|
42 |
|
|
43 |
45 |
const leading_dash = deco.path[0] === "/";
|
44 |
46 |
deco.trailing_dash = deco.path[deco.path.length - 1] === "/";
|
45 |
|
deco.path = deco.path.split("/").filter(s => s !== "");
|
46 |
|
if (leading_dash || deco.path.length === 0)
|
47 |
|
deco.path.unshift("");
|
48 |
47 |
|
49 |
|
return deco;
|
50 |
|
}
|
|
48 |
if (deco.domain) {
|
|
49 |
if (deco.domain.length > MAX_DOMAIN_CHARS) {
|
|
50 |
const idx = deco.domain.indexOf(".", deco.domain.length -
|
|
51 |
MAX_DOMAIN_CHARS);
|
|
52 |
if (idx === -1)
|
|
53 |
deco.domain = [];
|
|
54 |
else
|
|
55 |
deco.domain = deco.domain.substring(idx + 1);
|
51 |
56 |
|
52 |
|
/* Be sane: both arguments should be arrays of length >= 2 */
|
53 |
|
function domain_matches(url_domain, pattern_domain)
|
54 |
|
{
|
55 |
|
const length_difference = url_domain.length - pattern_domain.length;
|
56 |
|
|
57 |
|
for (let i = 1; i <= url_domain.length; i++) {
|
58 |
|
const url_part = url_domain[url_domain.length - i];
|
59 |
|
const pattern_part = pattern_domain[pattern_domain.length - i];
|
60 |
|
|
61 |
|
if (pattern_domain.length === i) {
|
62 |
|
if (pattern_part === "*")
|
63 |
|
return length_difference === 0;
|
64 |
|
if (pattern_part === "**")
|
65 |
|
return length_difference > 0;
|
66 |
|
if (pattern_part === "***")
|
67 |
|
return true;
|
68 |
|
return length_difference === 0 && pattern_part === url_part;
|
|
57 |
deco.domain_truncated = true;
|
69 |
58 |
}
|
70 |
59 |
|
71 |
|
if (pattern_part !== url_part)
|
72 |
|
return false;
|
73 |
|
}
|
74 |
|
|
75 |
|
return pattern_domain.length === url_domain.length + 1 &&
|
76 |
|
pattern_domain[0] === "***";
|
77 |
|
}
|
78 |
|
|
79 |
|
function path_matches(url_path, url_trailing_dash,
|
80 |
|
pattern_path, pattern_trailing_dash)
|
81 |
|
{
|
82 |
|
const dashes_ok = !(pattern_trailing_dash && !url_trailing_dash);
|
83 |
|
|
84 |
|
if (pattern_path.length === 0)
|
85 |
|
return url_path.length === 0 && dashes_ok;
|
86 |
|
|
87 |
|
const length_difference = url_path.length - pattern_path.length;
|
88 |
|
|
89 |
|
for (let i = 0; i < url_path.length; i++) {
|
90 |
|
if (pattern_path.length === i + 1) {
|
91 |
|
if (pattern_path[i] === "*")
|
92 |
|
return length_difference === 0;
|
93 |
|
if (pattern_path[i] === "**") {
|
94 |
|
return length_difference > 0 ||
|
95 |
|
(url_path[i] === "**" && dashes_ok);
|
96 |
|
}
|
97 |
|
if (pattern_path[i] === "***")
|
98 |
|
return length_difference >= 0;
|
99 |
|
return length_difference === 0 &&
|
100 |
|
pattern_path[i] === url_path[i] && dashes_ok;
|
|
60 |
if (deco.path.length > MAX_URL_PATH_CHARS) {
|
|
61 |
deco.path = deco.path.substring(0, deco.path.lastIndexOf("/"));
|
|
62 |
deco.path_truncated = true;
|
101 |
63 |
}
|
102 |
|
|
103 |
|
if (pattern_path[i] !== url_path[i])
|
104 |
|
return false;
|
105 |
64 |
}
|
106 |
65 |
|
107 |
|
return false;
|
108 |
|
}
|
109 |
|
|
110 |
|
function url_matches(url, pattern)
|
111 |
|
{
|
112 |
|
const url_deco = deconstruct_url(url);
|
113 |
|
const pattern_deco = deconstruct_url(pattern);
|
114 |
|
|
115 |
|
if (url_deco === undefined || pattern_deco === undefined) {
|
116 |
|
console.log(`bad comparison: ${url} and ${pattern}`);
|
117 |
|
return false
|
|
66 |
if (typeof deco.domain === "string") {
|
|
67 |
deco.domain = deco.domain.split(".");
|
|
68 |
if (deco.domain.splice(0, deco.domain.length - MAX_DOMAIN_LEN).length
|
|
69 |
> 0)
|
|
70 |
deco.domain_truncated = true;
|
118 |
71 |
}
|
119 |
72 |
|
120 |
|
return pattern_deco.proto === url_deco.proto &&
|
121 |
|
!(pattern_deco.proto === "file" && pattern_deco.trailing_dash) &&
|
122 |
|
!!url_deco.domain === !!pattern_deco.domain &&
|
123 |
|
(!url_deco.domain ||
|
124 |
|
domain_matches(url_deco.domain, pattern_deco.domain)) &&
|
125 |
|
path_matches(url_deco.path, url_deco.trailing_dash,
|
126 |
|
pattern_deco.path, pattern_deco.trailing_dash);
|
|
73 |
deco.path = deco.path.split("/").filter(s => s !== "");
|
|
74 |
if (deco.domain && deco.path.splice(MAX_URL_PATH_LEN).length > 0)
|
|
75 |
deco.path_truncated = true;
|
|
76 |
if (leading_dash || deco.path.length === 0)
|
|
77 |
deco.path.unshift("");
|
|
78 |
|
|
79 |
return deco;
|
127 |
80 |
}
|
128 |
81 |
|
129 |
|
function* each_domain_pattern(domain_segments)
|
|
82 |
function* each_domain_pattern(deco)
|
130 |
83 |
{
|
131 |
|
for (let slice = 0; slice < domain_segments.length; slice++) {
|
132 |
|
const domain_part = domain_segments.slice(slice).join(".");
|
|
84 |
for (let slice = 0; slice < deco.domain.length - 1; slice++) {
|
|
85 |
const domain_part = deco.domain.slice(slice).join(".");
|
133 |
86 |
const domain_wildcards = [];
|
134 |
|
if (slice === 0)
|
|
87 |
if (slice === 0 && !deco.domain_truncated)
|
135 |
88 |
yield domain_part;
|
136 |
|
if (slice === 1)
|
|
89 |
if (slice === 1 && !deco.domain_truncated)
|
137 |
90 |
yield "*." + domain_part;
|
138 |
91 |
if (slice > 1)
|
139 |
92 |
yield "**." + domain_part;
|
... | ... | |
141 |
94 |
}
|
142 |
95 |
}
|
143 |
96 |
|
144 |
|
function* each_path_pattern(path_segments, trailing_dash)
|
|
97 |
function* each_path_pattern(deco)
|
145 |
98 |
{
|
146 |
|
for (let slice = path_segments.length; slice > 0; slice--) {
|
147 |
|
const path_part = path_segments.slice(0, slice).join("/");
|
|
99 |
for (let slice = deco.path.length; slice > 0; slice--) {
|
|
100 |
const path_part = deco.path.slice(0, slice).join("/");
|
148 |
101 |
const path_wildcards = [];
|
149 |
|
if (slice === path_segments.length) {
|
150 |
|
if (trailing_dash)
|
|
102 |
if (slice === deco.path.length && !deco.path_truncated) {
|
|
103 |
if (deco.trailing_dash)
|
151 |
104 |
yield path_part + "/";
|
152 |
105 |
yield path_part;
|
153 |
106 |
}
|
154 |
|
if (slice === path_segments.length - 1 && path_segments[slice] !== "*")
|
|
107 |
if (slice === deco.path.length - 1 && !deco.path_truncated &&
|
|
108 |
deco.path[slice] !== "*")
|
155 |
109 |
yield path_part + "/*";
|
156 |
|
if (slice < path_segments.length - 1)
|
|
110 |
if (slice < deco.path.length - 1)
|
157 |
111 |
yield path_part + "/**";
|
158 |
|
if (slice < path_segments.length - 1 ||
|
159 |
|
path_segments[path_segments.length - 1] !== "***")
|
|
112 |
if (slice !== deco.path.length - 1 || deco.path_truncated ||
|
|
113 |
deco.path[slice] !== "***")
|
160 |
114 |
yield path_part + "/***";
|
161 |
115 |
}
|
162 |
116 |
}
|
... | ... | |
167 |
121 |
const deco = deconstruct_url(url);
|
168 |
122 |
|
169 |
123 |
if (deco === undefined) {
|
170 |
|
console.log("bad url format", url);
|
|
124 |
console.error("bad url format", url);
|
171 |
125 |
return false;
|
172 |
126 |
}
|
173 |
127 |
|
174 |
|
const all_domains = deco.domain ? each_domain_pattern(deco.domain) : [""];
|
|
128 |
const all_domains = deco.domain ? each_domain_pattern(deco) : [""];
|
175 |
129 |
for (const domain of all_domains) {
|
176 |
|
for (const path of each_path_pattern(deco.path, deco.trailing_dash))
|
|
130 |
for (const path of each_path_pattern(deco))
|
177 |
131 |
yield `${deco.proto}://${domain}${path}`;
|
178 |
132 |
}
|
179 |
133 |
}
|
180 |
134 |
|
181 |
135 |
/*
|
182 |
136 |
* EXPORTS_START
|
183 |
|
* EXPORT url_matches
|
184 |
137 |
* EXPORT each_url_pattern
|
185 |
138 |
* EXPORTS_END
|
186 |
139 |
*/
|
limit allowed pattern lengths