vuln-regex-detector
Advanced tools
Comparing version 1.2.0 to 1.3.0
# v1 | ||
## v1.3 | ||
### v1.3.0 | ||
*Additions* | ||
- Change config param to make the cache configurable. This is a breaking change. | ||
Contributors: | ||
- [Jamie Davis](davisjam@vt.edu) | ||
## v1.2 | ||
@@ -4,0 +15,0 @@ |
{ | ||
"name": "vuln-regex-detector", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"description": "Detect vulnerable regexes by querying a service hosted at Virginia Tech.", | ||
@@ -44,4 +44,5 @@ "main": "vuln-regex-detector-client.js", | ||
"mocha": "^5.0.5", | ||
"eslint-plugin-mocha": "^5.0.0" | ||
"eslint-plugin-mocha": "^5.0.0", | ||
"remove": "^0.1.5" | ||
} | ||
} |
@@ -14,3 +14,19 @@ # Summary | ||
vulnRegexDetector.test('(a+)+$') | ||
const regex = /(a+)+$/; // RegExp | ||
const pattern = regex.source; // String | ||
const cacheConfig = { | ||
type: vulnRegexDetector.cacheTypes.persistent | ||
}; | ||
const config = { | ||
cache: cacheConfig | ||
}; | ||
/* This runs synchronously so it's expensive. | ||
* It uses a persistent cache, so subsequent queries in this process or another one | ||
* can be resolved locally. */ | ||
const result = vulnRegexDetector.testSync(regex, config); | ||
console.log(`I got ${result}`); | ||
vulnRegexDetector.test(pattern, config) | ||
.then((result) => { | ||
@@ -30,4 +46,5 @@ if (result === vulnRegexDetector.responses.vulnerable) { | ||
The module exports: | ||
- functions `test` and `testSync` | ||
- a set of responses `responses` | ||
- functions `test` and `testSync` for making queries | ||
- macro `cacheTypes` for use specifying config.cache | ||
- a set of responses `responses` for interpreting results | ||
@@ -38,7 +55,20 @@ ## test | ||
/** | ||
* @regex: RegExp or string (e.g. /re/ or 're') | ||
* @config: object with fields: hostname port | ||
* default: 'toybox.cs.vt.edu', '8000' | ||
* @param regex: RegExp or string (e.g. /re/ or 're') | ||
* @param [config]: provide a config object like this: | ||
* { | ||
* server: { | ||
* hostname: 'toybox.cs.vt.edu', | ||
* port: 8000 | ||
* }, | ||
* cache: { | ||
* type: cacheTypes.persistent, | ||
* [persistentDir]: '/tmp/vuln-regex-detector-client-persistentCache' | ||
* } | ||
* } | ||
* | ||
* returns a Promise fulfilled with a vulnerable/safe/unknown or rejected with invalid. | ||
* Config defaults if not provided: | ||
* server: indicated in the example. This is a research server at Virginia Tech. | ||
* cache: 'persistent' with persistentDir in a subdir of os.tmpdir(). | ||
* | ||
* @returns Promise fulfilled with responses.X or rejected with responses.invalid. | ||
*/ | ||
@@ -52,7 +82,8 @@ vulnRegexDetector.test (regex, config) | ||
/** | ||
* @regex: RegExp or string (e.g. /re/ or 're') | ||
* @config: object with fields: hostname port | ||
* default: 'toybox.cs.vt.edu', '8000' | ||
* @param regex: see checkRegex API | ||
* @param [config]: see checkRegex API | ||
* | ||
* returns with vulnerable/safe/unknown/invalid. | ||
* @returns synchronous result: responses.X | ||
* | ||
* Since this makes a synchronous HTTP query it will be slow. | ||
*/ | ||
@@ -62,4 +93,9 @@ vulnRegexDetector.testSync (regex, config) | ||
NB: This API makes synchronous HTTP queries, which can be slow. You should probably not use it. | ||
NB: This API makes synchronous HTTP queries, which can be slow. You should not use it in server software. | ||
On an AWS micro instance this API can be called about 200 times per minute. | ||
This API is intended for use in CI contexts where performance is less critical. | ||
For use in CI, see [this module](https://www.npmjs.com/package/eslint-plugin-vuln-regex-detector). | ||
If your application defines many regexes dynamically you might want to write your own CI stage. | ||
## responses | ||
@@ -115,4 +151,8 @@ | ||
# Contributing | ||
Issues and PRs welcome. | ||
# License | ||
MIT |
180
test/test.js
@@ -5,2 +5,6 @@ /* eslint-env node, mocha */ | ||
const assert = require('assert'); | ||
const os = require('os'); | ||
const path = require('path'); | ||
const fs = require('fs'); | ||
const remove = require('remove'); | ||
@@ -59,3 +63,3 @@ // Outcome must be SAFE or UNKNOWN | ||
it('should accept config', () => { | ||
return vulnRegexDetector.test('abc', { hostname: 'toybox.cs.vt.edu', port: 8000 }) | ||
return vulnRegexDetector.test('abc', { server: { hostname: vulnRegexDetector.defaultServerConfig.hostname, port: 8000 } }) | ||
.then(assertIsOK, assertIsOK); | ||
@@ -94,5 +98,14 @@ }); | ||
it('should reject an invalid host', () => { | ||
return vulnRegexDetector.test('abcde', { hostname: 'no such host', port: 8000 }) | ||
let invalidConfig = { | ||
server: { | ||
hostname: 'no such host', | ||
port: 1 | ||
}, | ||
cache: { | ||
type: vulnRegexDetector.cacheTypes.none // Otherwise the default persistent cache will save us! | ||
} | ||
}; | ||
return vulnRegexDetector.test('abcde', invalidConfig) | ||
.then((response) => { | ||
assert.ok(false, `Invalid config should not have resolved (with ${response})`); | ||
assert.ok(false, `Invalid config ${JSON.stringify(invalidConfig)} should not have resolved (with ${response})`); | ||
}, (err) => { | ||
@@ -104,5 +117,14 @@ assert.ok(err === vulnRegexDetector.responses.invalid, `Invalid config rejected, but with ${err}`); | ||
it('should reject an invalid port', () => { | ||
return vulnRegexDetector.test('abcde', { hostname: 'toybox.cs.vt.edu', port: 22 }) | ||
let invalidConfig = { | ||
server: { | ||
hostname: vulnRegexDetector.defaultServerConfig.hostname, | ||
port: 22 | ||
}, | ||
cache: { | ||
type: vulnRegexDetector.cacheTypes.none // Otherwise the default persistent cache will save us! | ||
} | ||
}; | ||
return vulnRegexDetector.test('abcde', invalidConfig) | ||
.then((response) => { | ||
assert.ok(false, `Invalid config should not have resolved (with ${response})`); | ||
assert.ok(false, `Invalid config ${JSON.stringify(invalidConfig)} should not have resolved (with ${response})`); | ||
}, (err) => { | ||
@@ -134,3 +156,3 @@ assert.ok(err === vulnRegexDetector.responses.invalid, `Invalid config rejected, but with ${err}`); | ||
it('should accept config', () => { | ||
assertIsOK(vulnRegexDetector.testSync('abc', { hostname: 'toybox.cs.vt.edu', port: 8000 })); | ||
assertIsOK(vulnRegexDetector.testSync('abc', { server: { hostname: vulnRegexDetector.defaultServerConfig.hostname, port: 8000 } })); | ||
}); | ||
@@ -163,9 +185,27 @@ }); | ||
it('should reject an invalid host', () => { | ||
const response = vulnRegexDetector.testSync('abcde', { hostname: 'no such host', port: 8000 }); | ||
assert.ok(response === vulnRegexDetector.responses.invalid, `Invalid config returned ${response}`); | ||
let invalidConfig = { | ||
server: { | ||
hostname: 'no such host', | ||
port: 1 | ||
}, | ||
cache: { | ||
type: vulnRegexDetector.cacheTypes.none // Otherwise the default persistent cache will save us! | ||
} | ||
}; | ||
const response = vulnRegexDetector.testSync('abcde', invalidConfig); | ||
assert.ok(response === vulnRegexDetector.responses.invalid, `Invalid config ${JSON.stringify(invalidConfig)} returned ${response}`); | ||
}); | ||
it('should reject an invalid port', () => { | ||
const response = vulnRegexDetector.testSync('abcde', { hostname: 'toybox.cs.vt.edu', port: 22 }); | ||
assert.ok(response === vulnRegexDetector.responses.invalid, `Invalid config returned ${response}`); | ||
let invalidConfig = { | ||
server: { | ||
hostname: vulnRegexDetector.defaultServerConfig.hostname, | ||
port: 22 | ||
}, | ||
cache: { | ||
type: vulnRegexDetector.cacheTypes.none // Otherwise the default persistent cache will save us! | ||
} | ||
}; | ||
const response = vulnRegexDetector.testSync('abcde', invalidConfig); | ||
assert.ok(response === vulnRegexDetector.responses.invalid, `Invalid config ${JSON.stringify(invalidConfig)} returned ${response}`); | ||
}); | ||
@@ -175,10 +215,122 @@ }); | ||
describe('cache', () => { | ||
it('should hit cache on successive duplicate queries', () => { | ||
for (let i = 0; i < 10; i++) { | ||
assertIsSafeOrUnknown(vulnRegexDetector.testSync('abc')); | ||
} | ||
describe('persistent', () => { | ||
it('should hit cache instead of failing when config.server is invalid', () => { | ||
const pattern = 'abc'; | ||
// Make sync query to prime local persistent cache. | ||
let validConfig = { cache: { type: vulnRegexDetector.cacheTypes.persistent } }; | ||
const response1 = vulnRegexDetector.testSync(pattern, validConfig); | ||
assert.ok(response1 === vulnRegexDetector.responses.safe, `Error, unexpected response for sync query: ${response1}`); | ||
let invalidConfig = { | ||
server: { | ||
hostname: 'no such host', | ||
port: 1 | ||
}, | ||
cache: { | ||
type: vulnRegexDetector.cacheTypes.persistent | ||
} | ||
}; | ||
const response2 = vulnRegexDetector.testSync(pattern, invalidConfig); | ||
assert.ok(response2 === vulnRegexDetector.responses.safe, `Query failed: response ${response2}, probably due to my invalid config.server (so cache failed)`); | ||
}); | ||
it('honors persistentDir', () => { | ||
const pattern = 'abc'; | ||
const persistentDir = path.join(os.tmpdir(), `vuln-regex-detector-TEST-${process.pid}`); | ||
const cacheConfig = { | ||
type: vulnRegexDetector.cacheTypes.persistent, | ||
persistentDir: persistentDir | ||
}; | ||
function persistentDirExists () { | ||
try { | ||
return fs.statSync(persistentDir).isDirectory(); | ||
} catch (e) { | ||
return false; | ||
} | ||
} | ||
if (persistentDirExists()) { | ||
remove.removeSync(persistentDir); | ||
} | ||
// Make sync query to prime local persistent cache. | ||
const response1 = vulnRegexDetector.testSync(pattern, { cache: cacheConfig }); | ||
assert.ok(response1 === vulnRegexDetector.responses.safe, `Error, unexpected response for sync query: ${response1}`); | ||
assert.ok(persistentDirExists(), `Error, persistentDir ${persistentDir} does not exist after sync query`); | ||
// Now an invalid config should work. | ||
let invalidConfig = { | ||
server: { | ||
hostname: 'no such host', | ||
port: 1 | ||
}, | ||
cache: cacheConfig | ||
}; | ||
const response2 = vulnRegexDetector.testSync(pattern, invalidConfig); | ||
assert.ok(response2 === vulnRegexDetector.responses.safe, `Query failed: response ${response2}, probably due to my invalid config.server (so cache failed)`); | ||
// Clean up. | ||
remove.removeSync(persistentDir); | ||
// Now a query with an invalid config should fail. | ||
const response3 = vulnRegexDetector.testSync(pattern, invalidConfig); | ||
assert.ok(response3 === vulnRegexDetector.responses.invalid, `Query succeeded? response ${response3}`); | ||
}); | ||
}); | ||
describe('memory', () => { | ||
it('should hit cache instead of failing when config.server is invalid', () => { | ||
const pattern = 'abc'; | ||
// Make sync query to prime local persistent cache. | ||
let validConfig = { cache: { type: vulnRegexDetector.cacheTypes.memory } }; | ||
const response1 = vulnRegexDetector.testSync(pattern, validConfig); | ||
assert.ok(response1 === vulnRegexDetector.responses.safe, `Error, unexpected response for sync query: ${response1}`); | ||
let invalidConfig = { | ||
server: { | ||
hostname: 'no such host', | ||
port: 1 | ||
}, | ||
cache: { | ||
type: vulnRegexDetector.cacheTypes.memory | ||
} | ||
}; | ||
const response2 = vulnRegexDetector.testSync(pattern, invalidConfig); | ||
assert.ok(response2 === vulnRegexDetector.responses.safe, `Query failed: response ${response2}, probably due to my invalid config.server (so cache failed)`); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('defaultServerConfig', () => { | ||
it('has hostname', () => { | ||
return assert.ok(vulnRegexDetector.defaultServerConfig.hostname, 'Missing hostname'); | ||
}); | ||
it('has port', () => { | ||
return assert.ok(vulnRegexDetector.defaultServerConfig.port, 'Missing port'); | ||
}); | ||
}); | ||
describe('defaultCacheConfig', () => { | ||
it('has type', () => { | ||
return assert.ok(vulnRegexDetector.defaultCacheConfig.type, 'Missing type'); | ||
}); | ||
}); | ||
describe('cacheTypes', () => { | ||
it('has persistent', () => { | ||
return assert.ok(vulnRegexDetector.cacheTypes.persistent, 'Missing persistent'); | ||
}); | ||
it('has memory', () => { | ||
return assert.ok(vulnRegexDetector.cacheTypes.memory, 'Missing memory'); | ||
}); | ||
it('has none', () => { | ||
return assert.ok(vulnRegexDetector.cacheTypes.none, 'Missing none'); | ||
}); | ||
}); | ||
describe('responses', () => { | ||
@@ -185,0 +337,0 @@ it('has vulnerable', () => { |
@@ -30,18 +30,25 @@ 'use strict'; | ||
const DEFAULT_CONFIG = { | ||
hostname: 'toybox.cs.vt.edu', | ||
port: 8000 | ||
}; | ||
/* Logging. */ | ||
const LOGGING = false; | ||
/* Cache config. */ | ||
/* Cache. */ | ||
const CACHE_TYPES = { | ||
none: 'none', | ||
persistent: 'persistent', | ||
memory: 'memory', | ||
persistent: 'persistent' | ||
none: 'none' | ||
}; | ||
const CACHE_TYPE = CACHE_TYPES.persistent; | ||
/* Cache: memory. */ | ||
/* Default config. */ | ||
const defaultServerConfig = { | ||
hostname: 'toybox.cs.vt.edu', | ||
port: 8000 | ||
}; | ||
const defaultCacheConfig = { | ||
type: CACHE_TYPES.persistent, | ||
persistentDir: path.join(os.tmpdir(), 'vuln-regex-detector-client-persistentCache') | ||
}; | ||
/********** | ||
@@ -53,22 +60,35 @@ * Functions. | ||
* @param regex: RegExp or string (e.g. /re/ or 're') | ||
* @param config: object with fields: hostname port | ||
* default: 'toybox.cs.vt.edu', '8000' | ||
* @param [config]: provide a config object like this: | ||
* { | ||
* server: { | ||
* hostname: 'toybox.cs.vt.edu', | ||
* port: 8000 | ||
* }, | ||
* cache: { | ||
* type: cacheTypes.persistent, | ||
* [persistentDir]: '/tmp/vuln-regex-detector-client-persistentCache' | ||
* } | ||
* } | ||
* | ||
* returns a Promise fulfilled with a response or rejected with RESPONSE_INVALID. | ||
* Config defaults if not provided: | ||
* server: indicated in the example. This is a research server at Virginia Tech. | ||
* cache: 'persistent' with persistentDir in a subdir of os.tmpdir(). | ||
* | ||
* @returns Promise fulfilled with responses.X or rejected with responses.invalid. | ||
*/ | ||
function checkRegex (regex, config) { | ||
let _pattern; | ||
let _config; | ||
function checkRegex (_regex, _config) { | ||
let pattern; | ||
let config; | ||
/* Handle args. */ | ||
try { | ||
[_pattern, _config] = handleArgs(regex, config); | ||
[pattern, config] = handleArgs(_regex, _config); | ||
} catch (e) { | ||
return Promise.reject(RESPONSE_INVALID); | ||
} | ||
log(`Input OK. _pattern /${_pattern}/ _config ${JSON.stringify(_config)}`); | ||
log(`Input OK. pattern /${pattern}/ config ${JSON.stringify(config)}`); | ||
let postObject = generatePostObject(_pattern); | ||
let postObject = generatePostObject(pattern); | ||
let postBuffer = JSON.stringify(postObject); | ||
let postHeaders = generatePostHeaders(_config, Buffer.byteLength(postBuffer)); | ||
let postHeaders = generatePostHeaders(config, Buffer.byteLength(postBuffer)); | ||
@@ -80,3 +100,3 @@ // Wrapper so we can return a Promise. | ||
/* Check cache to avoid I/O. */ | ||
const cacheHit = checkCache(_pattern); | ||
const cacheHit = checkCache(config, pattern); | ||
if (cacheHit !== RESPONSE_UNKNOWN) { | ||
@@ -101,3 +121,3 @@ log(`Cache hit: ${cacheHit}`); | ||
log(`end: result ${result}`); | ||
updateCache(postObject.pattern, result); | ||
updateCache(config, postObject.pattern, result); | ||
@@ -128,24 +148,25 @@ if (result === RESPONSE_INVALID) { | ||
/** | ||
* @param regex: RegExp or string (e.g. /re/ or 're') | ||
* @param config: object with fields: hostname port | ||
* default: 'toybox.cs.vt.edu', '8000' | ||
* @param regex: see checkRegex API | ||
* @param [config]: see checkRegex API | ||
* | ||
* returns synchronous result: RESPONSE_X | ||
* @returns synchronous result: RESPONSE_X | ||
* | ||
* Since this makes a synchronous HTTP query it will be slow. | ||
*/ | ||
function checkRegexSync (regex, config) { | ||
let _pattern; | ||
let _config; | ||
function checkRegexSync (_regex, _config) { | ||
let pattern; | ||
let config; | ||
/* Handle args. */ | ||
try { | ||
[_pattern, _config] = handleArgs(regex, config); | ||
[pattern, config] = handleArgs(_regex, _config); | ||
} catch (e) { | ||
log(e); | ||
log(`Invalid input: _regex ${JSON.stringify(_regex)} _config ${JSON.stringify(_config)}`); | ||
return RESPONSE_INVALID; | ||
} | ||
log(`Input OK. _pattern /${_pattern}/ _config ${JSON.stringify(_config)}`); | ||
log(`Input OK. pattern /${pattern}/ config ${JSON.stringify(config)}`); | ||
/* Check cache to avoid I/O. */ | ||
const cacheHit = checkCache(_pattern); | ||
const cacheHit = checkCache(config, pattern); | ||
if (cacheHit !== RESPONSE_UNKNOWN) { | ||
@@ -156,5 +177,5 @@ log(`Cache hit: ${cacheHit}`); | ||
let postObject = generatePostObject(_pattern); | ||
let postObject = generatePostObject(pattern); | ||
let postBuffer = JSON.stringify(postObject); | ||
let postHeaders = generatePostHeaders(_config, Buffer.byteLength(postBuffer)); | ||
let postHeaders = generatePostHeaders(config, Buffer.byteLength(postBuffer)); | ||
let url = `https://${postHeaders.hostname}:${postHeaders.port}${postHeaders.path}`; | ||
@@ -183,3 +204,3 @@ | ||
const result = serverResponseToRESPONSE(responseBody); | ||
updateCache(postObject.pattern, result); | ||
updateCache(config, postObject.pattern, result); | ||
@@ -201,15 +222,16 @@ return result; | ||
* | ||
* Returns: [pattern, config] or throws exception | ||
* @returns: [pattern, config] or throws exception | ||
*/ | ||
function handleArgs (regex, config) { | ||
let _pattern; | ||
if (regex) { | ||
if (typeof regex === 'string') { | ||
_pattern = regex; | ||
function handleArgs (_regex, _config) { | ||
/* Identify regex pattern. */ | ||
let pattern; | ||
if (_regex) { | ||
if (typeof _regex === 'string') { | ||
pattern = _regex; | ||
} else { | ||
try { | ||
_pattern = regex.source; | ||
pattern = _regex.source; | ||
} catch (e) { | ||
log(`Invalid regex:`); | ||
log(regex); | ||
log(_regex); | ||
} | ||
@@ -220,3 +242,3 @@ } | ||
} | ||
if (!_pattern) { | ||
if (!pattern) { | ||
let errObj = { msg: 'Invalid args' }; | ||
@@ -226,13 +248,45 @@ throw errObj; | ||
// config | ||
let _config; | ||
if (config && config.hasOwnProperty('hostname') && config.hasOwnProperty('port')) { | ||
_config = config; | ||
/* Identify config. Accept a variety of flavors and fall back to defaults as needed. */ | ||
let config = {}; | ||
if (!_config) { | ||
config.server = defaultServerConfig; | ||
config.cache = defaultCacheConfig; | ||
} else { | ||
_config = DEFAULT_CONFIG; | ||
config.server = handleServerConfig(_config.server); | ||
config.cache = handleCacheConfig(_config.cache); | ||
} | ||
return [_pattern, _config]; | ||
return [pattern, config]; | ||
} | ||
/* Helper for handleArgs: config.server. */ | ||
function handleServerConfig (serverConfig) { | ||
if (!serverConfig) { | ||
return defaultServerConfig; | ||
} else if (!serverConfig.hasOwnProperty('hostname') || !serverConfig.hasOwnProperty('port')) { | ||
return defaultServerConfig; | ||
} | ||
return serverConfig; | ||
} | ||
/* Helper for handleArgs: config.cache. */ | ||
function handleCacheConfig (cacheConfig) { | ||
if (!cacheConfig) { | ||
return defaultCacheConfig; | ||
} | ||
// Must have valid type. | ||
if (!cacheConfig.hasOwnProperty('type') || !CACHE_TYPES.hasOwnProperty(cacheConfig.type)) { | ||
cacheConfig.type = CACHE_TYPES.persistent; | ||
} | ||
// If type is persistent, need persistentDir. | ||
if (cacheConfig.type === CACHE_TYPES.persistent && !cacheConfig.hasOwnProperty('persistentDir')) { | ||
cacheConfig.persistentDir = defaultCacheConfig.persistentDir; | ||
} | ||
return cacheConfig; | ||
} | ||
/* Return object to be sent over the wire as JSON. */ | ||
@@ -252,4 +306,4 @@ function generatePostObject (pattern) { | ||
const postHeaders = { | ||
hostname: config.hostname, | ||
port: config.port, | ||
hostname: config.server.hostname, | ||
port: config.server.port, | ||
path: '/api/lookup', | ||
@@ -293,24 +347,24 @@ method: 'POST', | ||
function useCache () { | ||
return CACHE_TYPE !== CACHE_TYPES.none; | ||
function useCache (config) { | ||
return config.cache.type !== CACHE_TYPES.none; | ||
} | ||
function updateCache (pattern, response) { | ||
if (!useCache()) { | ||
function updateCache (config, pattern, response) { | ||
if (!useCache(config)) { | ||
return; | ||
} | ||
return kvPut(pattern, response); | ||
return kvPut(config, pattern, response); | ||
} | ||
/* Returns RESPONSE_{VULNERABLE|SAFE} on hit, else RESPONSE_UNKNOWN on miss or disabled. */ | ||
function checkCache (pattern) { | ||
if (!useCache()) { | ||
function checkCache (config, pattern) { | ||
if (!useCache(config)) { | ||
return RESPONSE_UNKNOWN; | ||
} | ||
return kvGet(pattern); | ||
return kvGet(config, pattern); | ||
} | ||
function kvPut (key, value) { | ||
function kvPut (config, key, value) { | ||
/* Only cache VULNERABLE|SAFE responses. */ | ||
@@ -322,7 +376,7 @@ if (value !== RESPONSE_VULNERABLE && value !== RESPONSE_SAFE) { | ||
/* Put in the appropriate cache. */ | ||
switch (CACHE_TYPE) { | ||
switch (config.cache.type) { | ||
case CACHE_TYPES.persistent: | ||
return kvPutPersistent(config, key, value); | ||
case CACHE_TYPES.memory: | ||
return kvPutMemory(key, value); | ||
case CACHE_TYPES.persistent: | ||
return kvPutPersistent(key, value); | ||
default: | ||
@@ -333,9 +387,9 @@ return RESPONSE_UNKNOWN; | ||
function kvGet (key) { | ||
function kvGet (config, key) { | ||
/* Get from the appropriate cache. */ | ||
switch (CACHE_TYPE) { | ||
switch (config.cache.type) { | ||
case CACHE_TYPES.persistent: | ||
return kvGetPersistent(config, key); | ||
case CACHE_TYPES.memory: | ||
return kvGetMemory(key); | ||
case CACHE_TYPES.persistent: | ||
return kvGetPersistent(key); | ||
default: | ||
@@ -348,34 +402,20 @@ return RESPONSE_UNKNOWN; | ||
const PERSISTENT_CACHE_DIR = path.join(os.tmpdir(), 'vuln-regex-detector-client-persistentCache'); | ||
log(`PERSISTENT_CACHE_DIR ${PERSISTENT_CACHE_DIR}`); | ||
let kvPersistentInitialized = false; | ||
let kvPersistentCouldNotInitialize = false; | ||
/* Returns true if initialized, false on initialization failure. */ | ||
function initializeKVPersistent () { | ||
/* Tried before? */ | ||
if (kvPersistentInitialized) { | ||
return true; | ||
} | ||
if (kvPersistentCouldNotInitialize) { | ||
return false; | ||
} | ||
function initializeKVPersistent (config) { | ||
/* NB Makes FS syscalls each time in case config changes during lifetime. | ||
* Could cache the set of initialized dirs if this is a performance issue. */ | ||
/* First time through. */ | ||
/* First try a mkdir. Dir might exist already. */ | ||
try { | ||
fs.mkdirSync(PERSISTENT_CACHE_DIR); | ||
fs.mkdirSync(config.cache.persistentDir); | ||
} catch (e) { | ||
} | ||
/* If we have a dir now, we're happy. */ | ||
/* If we have a dir now, we're happy. | ||
* This also works if persistentDir is a symlink. */ | ||
try { | ||
const stats = fs.lstatSync(PERSISTENT_CACHE_DIR); | ||
const stats = fs.lstatSync(config.cache.persistentDir); | ||
if (stats.isDirectory()) { | ||
kvPersistentInitialized = true; | ||
return true; | ||
} else { | ||
kvPersistentCouldNotInitialize = true; | ||
return false; | ||
@@ -385,3 +425,2 @@ } | ||
/* Hmm. */ | ||
kvPersistentCouldNotInitialize = true; | ||
return false; | ||
@@ -391,3 +430,3 @@ } | ||
function kvPersistentFname (key) { | ||
function kvPersistentFname (config, key) { | ||
/* Need something we can safely use as a file name. | ||
@@ -399,8 +438,8 @@ * Keys are patterns and might contain /'s or \'s. | ||
const hash = crypto.createHash('md5').update(key).digest('hex'); | ||
const fname = path.join(PERSISTENT_CACHE_DIR, `${hash}.json`); | ||
const fname = path.join(config.cache.persistentDir, `${hash}.json`); | ||
return fname; | ||
} | ||
function kvPutPersistent (key, value) { | ||
if (!initializeKVPersistent()) { | ||
function kvPutPersistent (config, key, value) { | ||
if (!initializeKVPersistent(config)) { | ||
log(`kvPutPersistent: could not initialize`); | ||
@@ -413,3 +452,3 @@ return; | ||
* Hence the use of a tmp file and rename. */ | ||
const fname = kvPersistentFname(key); | ||
const fname = kvPersistentFname(config, key); | ||
const tmpFname = `${fname}-${process.pid}-tmp`; | ||
@@ -424,4 +463,4 @@ log(`kvPutPersistent: putting result in ${fname}`); | ||
function kvGetPersistent (key) { | ||
if (!initializeKVPersistent()) { | ||
function kvGetPersistent (config, key) { | ||
if (!initializeKVPersistent(config)) { | ||
return RESPONSE_UNKNOWN; | ||
@@ -431,3 +470,3 @@ } | ||
try { | ||
const fname = kvPersistentFname(key); | ||
const fname = kvPersistentFname(config, key); | ||
log(`kvGetPersistent: getting result from ${fname}`); | ||
@@ -445,7 +484,7 @@ const cont = JSON.parse(fs.readFileSync(fname)); | ||
* We do not cache RESPONSE_UNKNOWN or RESPONSE_INVALID responses since these might change. */ | ||
let memoryPattern2response = {}; | ||
let pattern2response = {}; | ||
function kvPutMemory (key, value) { | ||
if (!memoryPattern2response.hasOwnProperty(key)) { | ||
memoryPattern2response[key] = value; | ||
if (!pattern2response.hasOwnProperty(key)) { | ||
pattern2response[key] = value; | ||
} | ||
@@ -455,3 +494,3 @@ } | ||
function kvGetMemory (key) { | ||
const hit = memoryPattern2response[key]; | ||
const hit = pattern2response[key]; | ||
if (hit) { | ||
@@ -480,4 +519,16 @@ log(`kvGetMemory: hit: ${key} -> ${hit}`); | ||
module.exports = { | ||
/* Core APIs. */ | ||
test: checkRegex, | ||
testSync: checkRegexSync, | ||
/* Config. */ | ||
defaultServerConfig: defaultServerConfig, // makes testing easier | ||
defaultCacheConfig: defaultCacheConfig, // makes testing easier | ||
cacheTypes: { | ||
persistent: CACHE_TYPES.persistent, | ||
memory: CACHE_TYPES.memory, | ||
none: CACHE_TYPES.none | ||
}, | ||
/* Interpreting API responses. */ | ||
responses: { | ||
@@ -484,0 +535,0 @@ vulnerable: RESPONSE_VULNERABLE, |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
32769
747
153
9
2