config: use stricter arg and config file parsing.
This commit is contained in:
parent
b892aeab2f
commit
78dfe005c7
@ -135,7 +135,8 @@ Config.prototype.set = function set(key, value) {
|
||||
if (value == null)
|
||||
return;
|
||||
|
||||
key = key.toLowerCase().replace(/-/g, '');
|
||||
key = key.replace(/-/g, '');
|
||||
key = key.toLowerCase();
|
||||
|
||||
this.options[key] = value;
|
||||
};
|
||||
@ -156,7 +157,8 @@ Config.prototype.has = function has(key) {
|
||||
|
||||
assert(typeof key === 'string', 'Key must be a string.');
|
||||
|
||||
key = key.toLowerCase().replace(/-/g, '');
|
||||
key = key.replace(/-/g, '');
|
||||
key = key.toLowerCase();
|
||||
|
||||
if (this.hash[key] != null)
|
||||
return true;
|
||||
@ -214,7 +216,8 @@ Config.prototype.get = function get(key, fallback) {
|
||||
|
||||
assert(typeof key === 'string', 'Key must be a string.');
|
||||
|
||||
key = key.toLowerCase().replace(/-/g, '');
|
||||
key = key.replace(/-/g, '');
|
||||
key = key.toLowerCase();
|
||||
|
||||
if (this.hash[key] != null)
|
||||
return this.hash[key];
|
||||
@ -735,47 +738,69 @@ Config.prototype.location = function location(file) {
|
||||
Config.prototype.parseConfig = function parseConfig(text) {
|
||||
assert(typeof text === 'string', 'Config must be text.');
|
||||
|
||||
text = text.trim();
|
||||
if (text.charCodeAt(0) === 0xfeff)
|
||||
text = text.substring(1);
|
||||
|
||||
const parts = text.split(/\n+/);
|
||||
text = text.replace(/\r\n/g, '\n');
|
||||
text = text.replace(/\r/g, '\n');
|
||||
text = text.replace(/\\\n/g, '');
|
||||
|
||||
for (let line of parts) {
|
||||
line = line.trim();
|
||||
let colons = true;
|
||||
let seen = false;
|
||||
let num = 0;
|
||||
|
||||
for (const chunk of text.split('\n')) {
|
||||
const line = chunk.trim();
|
||||
|
||||
num += 1;
|
||||
|
||||
if (line.length === 0)
|
||||
continue;
|
||||
|
||||
if (/^\s*#/.test(line))
|
||||
if (line[0] === '#')
|
||||
continue;
|
||||
|
||||
let eq = line.indexOf('=');
|
||||
const col = line.indexOf(':');
|
||||
const equal = line.indexOf('=');
|
||||
const colon = line.indexOf(':');
|
||||
|
||||
if (col !== -1 && (col < eq || eq === -1))
|
||||
eq = col;
|
||||
let index = -1;
|
||||
|
||||
let key, value;
|
||||
if (eq === -1) {
|
||||
key = line.trim();
|
||||
value = '';
|
||||
if (colon !== -1 && (colon < equal || equal === -1)) {
|
||||
if (seen && !colons)
|
||||
throw new Error(`Expected \`=\` on line ${num}: "${line}".`);
|
||||
|
||||
index = colon;
|
||||
seen = true;
|
||||
colons = true;
|
||||
} else if (equal !== -1) {
|
||||
if (seen && colons)
|
||||
throw new Error(`Expected \`:\` on line ${num}: "${line}".`);
|
||||
|
||||
index = equal;
|
||||
seen = true;
|
||||
colons = false;
|
||||
} else {
|
||||
key = line.substring(0, eq).trim();
|
||||
value = line.substring(eq + 1).trim();
|
||||
const symbol = colons ? ':' : '=';
|
||||
throw new Error(`Expected \`${symbol}\` on line ${num}: "${line}".`);
|
||||
}
|
||||
|
||||
key = key.replace(/\-/g, '').toLowerCase();
|
||||
let key = line.substring(0, index).trim();
|
||||
|
||||
key = key.replace(/\-/g, '')
|
||||
|
||||
if (!isLowerKey(key))
|
||||
throw new Error(`Invalid option on line ${num}: ${key}.`);
|
||||
|
||||
const value = line.substring(index + 1).trim();
|
||||
|
||||
if (value.length === 0)
|
||||
continue;
|
||||
|
||||
const alias = Config.alias[key];
|
||||
|
||||
if (alias)
|
||||
key = alias;
|
||||
|
||||
if (key.length === 0)
|
||||
continue;
|
||||
|
||||
if (value.length === 0)
|
||||
continue;
|
||||
|
||||
this.data[key] = value;
|
||||
}
|
||||
};
|
||||
@ -816,19 +841,21 @@ Config.prototype.parseArg = function parseArg(argv) {
|
||||
}
|
||||
|
||||
if (arg.indexOf('--') === 0) {
|
||||
// e.g. --opt
|
||||
const parts = arg.split('=');
|
||||
const index = arg.indexOf('=');
|
||||
|
||||
let key = parts[0];
|
||||
let key = null;
|
||||
let value = null;
|
||||
let empty = false;
|
||||
|
||||
if (parts.length > 1) {
|
||||
if (index !== -1) {
|
||||
// e.g. --opt=val
|
||||
value = parts.slice(1).join('=').trim();
|
||||
key = arg.substring(2, index);
|
||||
value = arg.substring(index + 1);
|
||||
last = null;
|
||||
empty = false;
|
||||
} else {
|
||||
// e.g. --opt
|
||||
key = arg.substring(2);
|
||||
value = 'true';
|
||||
last = null;
|
||||
empty = true;
|
||||
@ -836,8 +863,8 @@ Config.prototype.parseArg = function parseArg(argv) {
|
||||
|
||||
key = key.replace(/\-/g, '');
|
||||
|
||||
if (key.length === 0)
|
||||
continue;
|
||||
if (!isLowerKey(key))
|
||||
throw new Error(`Invalid argument: --${key}.`);
|
||||
|
||||
if (value.length === 0)
|
||||
continue;
|
||||
@ -860,19 +887,32 @@ Config.prototype.parseArg = function parseArg(argv) {
|
||||
if (arg[0] === '-') {
|
||||
// e.g. -abc
|
||||
last = null;
|
||||
|
||||
for (let j = 1; j < arg.length; j++) {
|
||||
let key = arg[j];
|
||||
|
||||
if ((key < 'a' || key > 'z')
|
||||
&& (key < 'A' || key > 'Z')
|
||||
&& (key < '0' || key > '9')
|
||||
&& key !== '?') {
|
||||
throw new Error(`Invalid argument: -${key}.`);
|
||||
}
|
||||
|
||||
const alias = Config.alias[key];
|
||||
|
||||
if (alias)
|
||||
key = alias;
|
||||
|
||||
this.args[key] = 'true';
|
||||
|
||||
last = key;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// e.g. foo
|
||||
const value = arg.trim();
|
||||
const value = arg;
|
||||
|
||||
if (value.length === 0) {
|
||||
last = null;
|
||||
@ -908,24 +948,24 @@ Config.prototype.parseEnv = function parseEnv(env) {
|
||||
assert(env && typeof env === 'object');
|
||||
|
||||
for (let key of Object.keys(env)) {
|
||||
let value = env[key];
|
||||
const value = env[key];
|
||||
|
||||
assert(typeof value === 'string');
|
||||
|
||||
if (key.indexOf(prefix) !== 0)
|
||||
if (!util.startsWith(key, prefix))
|
||||
continue;
|
||||
|
||||
key = key.substring(prefix.length);
|
||||
key = key.replace(/_/g, '').toLowerCase();
|
||||
key = key.replace(/_/g, '');
|
||||
|
||||
if (key.length === 0)
|
||||
if (!isUpperKey(key))
|
||||
continue;
|
||||
|
||||
value = value.trim();
|
||||
|
||||
if (value.length === 0)
|
||||
continue;
|
||||
|
||||
key = key.toLowerCase();
|
||||
|
||||
// Do not allow one-letter aliases.
|
||||
if (key.length > 1) {
|
||||
const alias = Config.alias[key];
|
||||
@ -989,27 +1029,30 @@ Config.prototype.parseForm = function parseForm(query, map) {
|
||||
if (query.length === 0)
|
||||
return;
|
||||
|
||||
if (query[0] === '?' || query[0] === '#')
|
||||
let ch = '?';
|
||||
|
||||
if (map === this.hash)
|
||||
ch = '#';
|
||||
|
||||
if (query[0] === ch)
|
||||
query = query.substring(1);
|
||||
|
||||
const parts = query.split('&');
|
||||
|
||||
for (const pair of parts) {
|
||||
for (const pair of query.split('&')) {
|
||||
const index = pair.indexOf('=');
|
||||
|
||||
let key, value;
|
||||
if (index === -1) {
|
||||
key = pair;
|
||||
value = '';
|
||||
} else {
|
||||
if (index !== -1) {
|
||||
key = pair.substring(0, index);
|
||||
value = pair.substring(index + 1);
|
||||
} else {
|
||||
key = pair;
|
||||
value = 'true';
|
||||
}
|
||||
|
||||
key = unescape(key);
|
||||
key = key.replace(/\-/g, '').toLowerCase();
|
||||
key = key.replace(/\-/g, '');
|
||||
|
||||
if (key.length === 0)
|
||||
if (!isLowerKey(key))
|
||||
continue;
|
||||
|
||||
value = unescape(value);
|
||||
@ -1055,6 +1098,24 @@ function isAlpha(str) {
|
||||
return /^[a-z0-9]+$/.test(str);
|
||||
}
|
||||
|
||||
function isKey(key) {
|
||||
return /^[a-zA-Z0-9]+$/.test(key);
|
||||
}
|
||||
|
||||
function isLowerKey(key) {
|
||||
if (!isKey(key))
|
||||
return false;
|
||||
|
||||
return !/[A-Z]/.test(key);
|
||||
}
|
||||
|
||||
function isUpperKey(key) {
|
||||
if (!isKey(key))
|
||||
return false;
|
||||
|
||||
return !/[a-z]/.test(key);
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
Loading…
Reference in New Issue
Block a user