Package Exports
- linvail
- linvail/instrument
- linvail/library
- linvail/runtime
Readme
Linvail
Linvail is a npm module which provides provenancial equality to JavaScript.
As many other languages, JavaScript supports two kinds of equality: structural equality which compares values based on their content and referential equality which compare value based on their memory location. Provenancial equality goes a step further and compares values based on their provenance. Provenancial equality can be seen as a stricter version of referential equality which is itself a stricter version of structural equality. In other words, the following implication holds:
(x eq_prov y) => (x eq_ref y) => (x eq_struct y)
Provenancial equality forms the foundation for advanced dynamic program analyses, such as taint analysis and concolic testing.
Basic Usage in Node
// main.mjs
import { is } from "linvail/library";
const foo1 = 123;
const foo2 = 123;
const bar = 456;
const array = [bar, foo1];
array.sort();
console.log(array[0]); // 123
console.log(array[1]); // 456
console.log(is(array[0], foo1)); // true
console.log(is(array[0], foo2)); // false> npm install linvail
> npx linvail main.mjs
123
456
true
falseConvenience CLI
Configuration options can be passed to the CLI using environment variables:
LINVAIL_INCLUDE: A comma-separated list of globs to designate the files where data povenance should be tracked. Default:"**/*".LINVAIL_EXCLUDE: A comma-separated list of globs to designate the files where data provenance should not be tracked; takes precedence overLINVAIL_INCLUDE. Default:"node_modules/**/*".LINVAIL_GLOBAL_OBJECT: Defines whether data provenance should be tracked across the global object and the global declarative record. Default:"external"."external": Leave the global object and the global declarative intact."internal": Replace the global object and the global declarative record with new objects that can track data provenance.
LINVAIL_GLOBAL_DYNAMIC_CODE: Defines whether data provenance should be tracked across global dynamic code. Note that data provenance is always tracked across local dynamic code (i.e.: strings passed to direct eval calls). Default:"internal".LINVAIL_COUNT: Defines whether tracked primitive values should embed a hidden__indexproperty which can only be observed when passing values toLinvail.dir. Default:false.
Convenience API
is(value1, value2): Provenancial equality.dir(value): Bypass the access control system and log the value to the console.Set,Map,WeakSet, andWeakMap: Similar to their standard counter part but keys are compared using provenancial equality.import { Set } from "linvail/library"; const foo = 123; const set = new Set([foo]); console.log(set.has(foo)); // true console.log(set.has(123)); // false
Core Usage
For more advanced use cases, the core API must be used. Reasons to use the core interface over the convenience interface include:
- Dynamic program analysis based on provenancial equality; the target program does not import the linvail library.
- Using a runtime other than Node.
- Overcoming limitations of the convenience interface.
// main.mjs
import { generate } from "astring";
import { parse } from "acorn";
import { setupile, transpile, retropile } from "aran";
import { createRuntime, weave } from "linvail";
const {
eval: evalGlobal,
console: { log, dir },
Reflect: { defineProperty },
} = globalThis;
const advice_global_variable = "__LINVAIL_ADVICE__";
const intrinsics = evalGlobal(generate(setupile()));
const { advice, library } = createRuntime(intrinsics, { dir });
defineProperty(globalThis, advice_global_variable, {
__proto__: null,
value: advice,
writable: false,
enumerable: false,
configurable: false,
});
const code = `(({ is }) => {
const foo1 = 123;
const foo2 = 123;
return is(foo1, foo1) && !is(foo1, foo2);
});`;
const main = evalGlobal(
generate(
retropile(
weave(
transpile({
path: "dynamic://eval/global",
kind: "eval",
situ: { type: "global" },
root: parse(code, { ecmaVersion: "latest" }),
}),
{ advice_global_variable },
),
),
),
);
log(main(library)); // true> npm instal acorn astring aran linvail
> node main.mjs
trueCore API
linvail/runtimecreateRuntime(intrinsics, options): Create a runtime object.intrinsics: An object containing intrinsic values provided by evaluating code generated byaran.setupile.options: A configuration object.dir: A function used to log values to the console.count: A boolean indicating whether tracked primitive values should embed a hidden__indexproperty for debugging purposes.
- Returns an object containing a custom linvail advice object and a Linvail library object. The advice should be made available as a global variable so that instrumented code can access it. If needed, the user is responsible for exposing the library to instrumented code.
toStandardAdvice(advice): Convert a custom Linvail advice into a standard Aran, allowingaran.weaveStandardto replacelinvail.weave.standard_pointcut: Standard Aran pointcut that should be provided toaran.weaveStandard.
linvail/instrumentweave(node, options): Instrument AranLang programs, the output expects a global variable holding a Linvail advice object.node: An AranLang program node.options: A configuration object.advice_global_variable: The name of the global variable containing the Linvail advice object.