I would like to create a basic .NET wrapper for the xkpasswd JavaScript library (see generate.js source-code here, and the live demo here). To accomplish this, I am using Microsoft ClearScript.
I have two problems. The first of them I think it is solved or partially solved...
The first problem I had is when trying to execute the generate.js file, I get an error message: "require is not defined
". I tried to solve the error by setting the document category to ModuleCategory.CommonJS
when executing the script document (engine.ExecuteDocument(".\generate.js", ModuleCategory.CommonJS)
) which I figured it out after I seen a similar solution in this answer; and also doing changes to get rid of 'fs' and 'path' modules from node.js, so this is the code that I'm executing now:
//var fs = require('fs');
//var path = require('path');
var defaultWordList = require('./xkpasswd-words.json');
// define helpers
var h = {
random: function random(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
},
getRandomWord: function getRandomWord(wordList) {
return wordList[h.random(0, wordList.length - 1)];
},
resolveComplexity: function resolveComplexity(complexity) {
// Patterns can consist of any combination of the following: (w)ords, (d)igits, (s)eparators
var compl = complexity || 2;
var rtn = {};
rtn.separators = '#.-=+_';
if (compl < 1) compl = 1;
if (compl > 6) compl = 6;
if (compl === 1) rtn.pattern = 'wsw';
if (compl === 2) rtn.pattern = 'wswsw';
if (compl === 3) rtn.pattern = 'wswswsdd';
if (compl === 4) rtn.pattern = 'wswswswsdd';
if (compl === 5) {
rtn.pattern = 'wswswswswsdd';
rtn.separators = '#.-=+_!$*:~?';
}
if (compl === 6) {
rtn.pattern = 'ddswswswswswsdd';
rtn.transform = 'alternate';
rtn.separators = '#.-=+_!$*:~?%^&;';
}
return rtn;
},
processOpts: function processOpts(opts) {
var rtn = {};
var complexity = parseInt(opts.complexity, 10);
complexity = typeof complexity === 'number' ? complexity : 3;
var predefined = h.resolveComplexity(complexity);
var separators = typeof opts.separators === 'string' ? opts.separators : predefined.separators;
rtn.pattern = opts.pattern || predefined.pattern;
rtn.separator = separators.split('')[h.random(0, separators.length - 1)];
rtn.transform = opts.transform || predefined.transform || 'lowercase';
rtn.wordList = opts.wordList;
return rtn;
},
// this needs to support the following options:
// 1) ""words.json""
// 2) ""words.txt""
// 3) ""orange,banana, fizz, buzz"" (string of comma-separated words)
readCustomWordList: function readCustomWordList(input) {
var data;
var rtn = [];
if (Array.isArray(input)) {
data = input;
}
// parse string input
if (typeof input === 'string') {
var tmpWordList = input.split(',');
if (tmpWordList.length === 1) {
//var targetFile = path.resolve(tmpWordList[0]);
var fileName = tmpWordList[0];
var shell = new ActiveXObject('WScript.Shell');
var targetFile = shell.CurrentDirectory + '\\' + fileName;
if (targetFile.indexOf('.json') === targetFile.length - 5) {
// eslint-disable-next-line
data = require(targetFile);
}
if (targetFile.indexOf('.txt') === targetFile.length - 4) {
//var fileContents = fs.readFileSync(targetFile).toString();
var fileSystem = new ActiveXObject('Scripting.FileSystemObject');
var file = fileSystem.OpenTextFile(targetFile, 1);
var fileContents = file.ReadAll();
file.Close();
data = fileContents.split('\n');
}
}
if (!data) {
data = tmpWordList;
}
}
// if there's no data return false
if (!data) {
return false;
}
// remove empty
for (var i = 0; i < data.length; i++) {
var word = typeof data[i] === 'string' ? data[i].trim() : '';
if (word.length) {
rtn.push(word);
}
}
return rtn;
}
};
module.exports = function main(opts) {
var o = h.processOpts(opts || {});
var pattern = o.pattern.split('');
var uppercase = (typeof o.transform === 'string' && o.transform.toLowerCase() === 'uppercase');
var password = [];
var wordList = defaultWordList;
var customWordList = h.readCustomWordList(o.wordList);
if (Array.isArray(customWordList) && customWordList.length) {
wordList = customWordList;
}
pattern.forEach(function generatePasswordSegment(type) {
var value;
if (type === 'd') value = h.random(0, 9);
if (type === 's') value = o.separator;
if (type === 'w' || type === 'W') {
value = h.getRandomWord(wordList);
if (typeof o.transform === 'string' && o.transform.toLowerCase() === 'alternate') {
uppercase = !uppercase;
}
if (uppercase || type === 'W') {
value = value.toUpperCase();
} else {
value = value.toLowerCase();
}
}
password.push(value);
});
return password.join('');
};
Note: I'm not sure if the changes I did will work as expected, anyway I don't require to read external word-list files in the readCustomWordList
functionality, so I could completely remove that code in the worst case and to load only a string array with the words.
The second and current problem I'm having, is that I get an error when trying to get the "main" function to use it. The error message says: main is not defined
.
This is the code I'm using:
Using engine As New V8ScriptEngine("xkpasswd_engine", V8ScriptEngineFlags.None)
engine.ExecuteDocument("\generate.js", ModuleCategory.CommonJS)
Dim mainFunction As ScriptObject =
DirectCast(engine.Evaluate("main"), ScriptObject)
Dim opts As Object = New With {
.complexity = 3,
.separators = "#.-=+_",
.pattern = "wswswsdd",
.transform = "lowercase",
.wordList = Nothing
}
Dim password As String =
CStr(mainFunction.Invoke(asConstructor:=False, opts))
Console.WriteLine("Generated password: " & password)
End Using
What I'm doing wrong and how to fix it?.
I think maybe it is just a matter of figuring out the correct naming to retrieve "main"?. I tried to get "module.exports" but I get a module is not defined
error too.
To access the exports of a CommonJS module, you have to call require
from another CommonJS module.
Instead of this:
engine.ExecuteDocument("generate", ModuleCategory.CommonJS)
Dim mainFunction = DirectCast(engine.Evaluate("main"), ScriptObject) 'ERROR
Try this:
Dim info As New DocumentInfo With {.Category = ModuleCategory.CommonJS}
Dim mainFunction = DirectCast(engine.Evaluate(info, "return require('generate')"), ScriptObject) 'OK
One other thing: Node.js can apparently treat JSON files as CommonJS modules, but ClearScript can't. To make this work, prepend module.exports =
to your copy of xkpasswd-words.json
. It should look like this:
module.exports = ["aa","aah", ...