npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

aran-access-control

v0.0.0

Published

Access Control System for Aran

Downloads

7

Readme

AranAccessControl

AranAccessControl is npm module that implements an access control system around JavaScript code instrumented by Aran. This module's motivation is to build dynamic analyses capable of tracking primitive values across the object graph.

Getting Started

npm install acorn aran astring aran-access-control 
const Acorn = require("acorn");
const Aran = require("aran");
const Astring = require("astring");
const AranAccessControl = require("aran-access-control");

const aran = Aran({namespace:"TRAPS", sandbox:true});
const instrument = (script, parent) =>
  Astring.generate(aran.weave(Acorn.parse(script), pointcut, parent));
let counter = 0;
const access = AranAccessControl({
  instrument: instrument,
  enter: (value) => ({concrete:value, shadow:"#"+(counter++)}),
  leave: (value) => value.concrete
});
const pointcut = Object.keys(access.advice);

global.TRAPS = Object.assign({}, access.advice);
global.TRAPS.primitive = (primitive, serial) => {
  const result = access.advice.primitive(primitive, serial);
  console.log(result.shadow+"("+result.concrete+") // @"+serial);
  return result;
};
global.TRAPS.binary = (operator, left, right, serial) => {
  const result = access.advice.binary(operator, left, right, serial);
  console.log(result.shadow+"("+result.concrete+") = "+left.shadow+" "+operator+" "+right.shadow+" // @"+serial);
  return result;
};

global.eval(Astring.generate(aran.setup()));
global.eval(instrument(`
let division = {};
division.dividend = 1 - 1;
division.divisor = 20 - 2 * 10;
division.result = division.dividend / division.divisor;
if (isNaN(division.result))
  console.log("!!!NaN division result!!!");
`));
#6(apply) // @2
#10(defineProperty) // @2
#14(getPrototypeOf) // @2
#18(keys) // @2
#22(iterator) // @2
#24(undefined) // @2
#26(dividend) // @6
#27(1) // @9
#28(1) // @10
#29(0) = #27 - #28 // @8
#30(divisor) // @12
#31(20) // @15
#32(2) // @17
#33(10) // @18
#34(20) = #32 * #33 // @16
#35(0) = #31 - #34 // @14
#36(result) // @20
#37(dividend) // @23
#38(divisor) // @25
#39(NaN) = #29 / #35 // @22
#42(result) // @30
#45(log) // @33
#46(!!!NaN division result!!!) // @35
!!!NaN division result!!!

Demonstrators

  • demo/analysis/identity.js Demonstrate the API of this module but don't produce any observable effect.
  • demo/analysis/tracer.js Use an identity membranes and log every operations.
  • demo/analysis/wrapper: Every values entering instrumented areas are wrapped to provide a well-defined identity. Every wrapper leaving instrumented areas are unwrapped so the behavior of the base program is not altered. Wrapping and unwrapping operations are logged.
  • demo/analysis/concolic: Same as above but also logs the arguments and result of triggered aran's traps. The resulting log is a detailed data-flow trace which with proper formating can be fed to a SMT solver.

API

catergory

  • A wild value is either:
    • a primitive
    • a reference which satisfies the below constraints:
      • its prototype is a wild value
      • the values of its data properties are wild values
      • the getters and setters of its accessor properties are wild values
      • applying it with a wild value this-argument and wild value arguments will return a wild value
      • constructing it with wild value arguments will return a wild value
  • A tame value is either:
    • a primitive
    • a reference which satisfies the below constraints:
      • its prototype is a tame value.
      • the values of its data properties are inner values
      • the getters and setters of its accessor properties are tame values
      • applying it with a tame value this-argument and inner value arguments will return an inner value
      • constructing it with inner value arguments will return an inner value

access = require("aran-access-control")(membrane)

  • membrane :: object
    • inner = membrane.enter(tame): User-defined function to convert a tame value to an inner value.
    • tame = membrane.leave(inner): User-defined function to convert an inner value to a tame value.
    • instrumented = membrane.instrument(code, serial): This function will be called to transforms code before passing it to the infamous eval function.
      • code :: string
      • serial :: number
      • instrumented :: string
  • access :: object
    • access.advice :: object An aran advice, contains Aran traps and a SANDBOX field which is set to access.capture(global). The user can modify the advice before leting aran using it.
    • access.membrane :: object: The same object as the membrane arguments.
    • tame = access.capture(wild) Convert a wild value into a tame value.
    • wild = access.release(tame): Convert a tame value into a wild value.

Discussion

Aran and program instrumentation in general is good for introspecting the control flow and pointers data flow. Things become more difficult when reasoning about primitive value data flow is involved. For instance, there is no way at the JavaScript language level to differentiate two null values even though they have different origins. This restriction strikes every JavaScript primitive values because they are inlined into different parts of the program's state -- e.g the environment and the value stack. All of these copying blur the concept of a primitive value's identity and lifetime. By opposition, objects can be properly differentiated based on their address in the store. Such situation happens in almost every mainstream programming languages. We now discuss several strategies to provide an identity to primitive values:

  • Shadow States: For low-level languages such as binary code, primitive values are often tracked by maintaining a so called "shadow state" that mirrors the concrete program state. This shadow state contains analysis-related information about the program values situated at the same location in the concrete state. Valgrind is a popular binary instrumentation framework which utilizes this technique to enables many data-flow analyses. The difficulty of this technique lies in maintaining the shadow state as non-instrumented functions are being executed. In JavaScript this problem typically arises when objects are passed to non instrumented functions such as builtins. Keeping the shadow store in sync during such operation requires to know the exact semantic of the non-instrumented function. Since they are so many different builtin functions in JavaScript, this is a very hard thing to do.
  • Record And Replay: Record and replay systems such as Jalangi are an intelligent response to the challenge of keeping in sync the shadow state with its concrete state. Acknowledging that divergences between shadow and concrete states cannot be completely avoided, these systems allows divergences in the replay phase which can be recovered from by utilizing the trace gathered during the record phase. We propose two arguments against such technique: First, every time divergences are resolved in the replay phase, values with unknown origin are being introduced which necessarily diminish the precision of the resulting analysis. Second, the replay phase only provide information about partial execution which can be puzzling to reason about.
  • Wrappers: Instead of providing an entire separated shadow state, wrappers constitute a finer grained solution. By wrapping primitive values inside objects we can simply let them propagate through the data flow of the base program. The challenge introduced by wrappers is to make them behave like their wrapped primitive value to non-instrumented code. We explore three solutions to this challenge:
    • Boxed Values: JavaScript enables to box booleans, numbers and strings. Despite that symbols, undefined and null cannot be tracked by this method, boxed values do not always behave like their primitive counterpart within builtins.
      // Strings cannot be differentiated based on their origin
      let string1 = "abc";
      let string2 = "abc";
      assert(string1 === string2);
      // Boxed strings can be differentiated based on their origin
      let boxed_string1 = new String("abc");
      let boxed_string2 = new String("abc");
      assert(boxed_string1 !== boxed_string2);
      // Boxed value behave as primitive in some builtins: 
      assert(JSON.stringify({a:string1}) === JSON.stringify({a:boxed_string1}));
      // In others, they don't...
      let error
      try {
        Object.defineProperty(string1, "foo", {value:"bar"});
      } catch (e) {
        error = e;
      }
      assert(error);
      Object.defineProperty(boxed_string1, "foo", {value:"bar"});
    • valueOf Method: A similar mechanism to boxed value is to use the valueOf method. Many JavaScript builtins expecting a primitive value but receiving an object will try to convert this object into a primitive using its valueOf method. As for boxed values this solution is not bullet proof and there exists many cases where the valueOf method will not be invoked.
    • Explicit Wrappers: Finally a last options consists in using explicit wrappers which should be cleaned up before escaping to non-instrumented code. This requires to setup an access control system between instrumented code and non-instrumented code. This the solution this module directly enables.

Acknowledgments

I'm Laurent Christophe a phd student at the Vrij Universiteit of Brussel (VUB). I'm working at the SOFT language lab in close relation with my promoters Coen De Roover and Wolfgang De Meuter. I'm currently being employed on the Tearless project.