| 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