@datadayrepos/js-lexer
TypeScript icon, indicating that this package has built-in type declarations

0.0.1-beta.14 • Public • Published

js-lexer

JS Lexer is a bespoke, security-conscious JavaScript expression parser designed for specific contexts.

🚀 Features

  • Security first: It allows only execution of safe expressions.
  • CSP Compliant: Fully compatible with the most stringent Content Security Policy (CSP) settings enforced by web servers.
  • Lightweight: A minimal footprint of just 3kb.
  • Its fast and optimized: Offers speed and optimization comparable to native JavaScript solutions.

Purpose and context

The primary goal of JS Lexer is to provide a secure parsing mechanism for JavaScript expressions. This necessity stems from the requirement to execute dynamically generated code within our editors, particularly focusing on logical operators and value comparisons within objects.

Traditional approaches in native JavaScript often clash with stringent CSP directives. The scarcity of secure parsers capable of replacing eval or new Function() constructors was notably surprising. These traditional methods are alarmingly prone to injection attacks and hijacking vulnerabilities. Although there is a debate among experts about the relative safety of modern new Function() constructs compared to the notoriously unsafe eval function, browser standard authorities deem them risky enough to warrant caution.

In our quest to deliver high-security applications, we recognized the need for a parser that not only supports dynamic services but also adheres to strict security standards. JS Lexer is our solution to this challenge, striking a balance between dynamic code execution and robust security measures.

Technically, the expression parser is based on the principles of the Shunting yard algorithm.

🛠 Usage

import { safeFunction } from '@datadayrepos/js-lexer'

// execute the function here and pass in necesarry arguments
const result = safeFunction(
  expression, // js like expression as string
  context, // main object
  subcontext, // sub object of parent
  langObject // special case for i18n translation parser
)

1. The objects

The objects in this context refer to any valid JavaScript object structure, with the exception of functions. This primarily includes object structures and arrays.

The term "subobject" is used to describe a subset of the main object, although it can, in principle, be an independent entity.

The expressions

Expressions within this framework are subject to certain restrictions:

Scope Limitation: Expressions can only reference the two objects passed into the function. They do not have access to global variables or any other external data. This limitation is central to the security model.

Syntax and Policies: First Context Object: The mandatory context object can be referred to in one of two ways:

  • Using the notation '$.path.to.something', with "$." as the initial path marker.
  • Alternatively, as "rootFormData."

Second Context Object: This object can be referenced as:

  • '$$$.path.to.something', with "$$$." as the initial path marker.
  • Or, as "parentFormData."

Expressions Involvement: It is feasible to construct expressions that reference either one or both of these objects.

Language Context Object: This is a special case. It will traverse the path of an i18n-style language object passed in: Using the expression marker 't()'.

Examples:

Here are some examples of expressions that conform to the specified syntax and policies:

Accessing an Array Element and Checking for a Value:

'rootFormData.array_1[0].multicheckbox_1.includes(\'value2\')'

This expression accesses the first element of array_1 in rootFormData and checks if multicheckbox_1 includes the value 'value2'.

Negating a Property Value:

'!rootFormData.c.bb'

Here, the expression negates the boolean value of property bb within the c object of rootFormData.

Comparing a Property Value to a Number:

'rootFormData.ll[3].tt < 5'

In this case, the expression checks if the tt property in the third index of the ll array in rootFormData is less than 5.

Ternary Expression:

rootFormData.status === 'active' ? 'Status is active' : 'Status is inactive'

The expression checks if rootFormData.status is equal to 'active'. If true, it evaluates to 'Status is active'. If false, it evaluates to 'Status is inactive'.

Language Parser Usage:

't(myNameSpace.c)'

This expression uses the language context object to retrieve a translated string, accessing the c property within the myNameSpace namespace.

Each of these examples demonstrates how to construct expressions within the defined syntax rules, enabling the manipulation and evaluation of JavaScript object properties in a secure manner.

Supported operations

These are the currently supported operations:

Comparison Operations These operations are used for comparing values:

!: Not, for negation or inequality checks.

: Greater than. <: Less than.

JavaScript Specific Operations Operations that are specific to JavaScript functionality:

.: Dot operator, currently supports .includes() method for array and string inclusion checks. t: Internationalization function, supports t() syntax for language translations.

Logical Operations These are used for logical reasoning and flow control:

&: Logical AND. :: Used in conditional (ternary) expressions. =: Assignment or equality. ?: Used in conditional (ternary) expressions. |: Logical OR.

Mathematical Operations Operations for performing mathematical calculations:

%: Modulus, for finding the remainder. (: Opening parenthesis for grouping expressions. ): Closing parenthesis for grouping expressions. *: Multiplication. +: Addition. -: Subtraction. /: Division.

String Operations Operations specifically for handling strings:

': Single quotes for defining string literals. `: Backticks for defining string literals. ": Double quotes for defining string literals.

Each of these operations plays a specific role in manipulating and evaluating expressions within your system, ensuring a structured and secure way to handle JavaScript expressions.

2. Install

npm install @datadayrepos/js-lexer

3. Develop

Your src directory is where your TypeScript source files reside. Transpiled files are output to the dist directory. The dist-test dir holds a larger output with test functions and objects.

💻 DEV Commands

Build: Transpile TypeScript to JavaScript

pnpm run build

Linting: Check and fix code style

pnpm run lint
pnpm run lint:fix

Release: Bump version and publish

pnpm run release

Testing: Run tests (tbd)

pnpm run test

Type Checking: Validate TypeScript

pnpm run typecheck

Publish: Publish package publicly

pnpm run pub

Test e2e: Builds tests-build and runs the tests of the parser and compiler

pnpm run test:e2e

Test e2e: Builds tests-build and runs the tests of the getvalue parser

pnpm run test:getPathVal

4. Todo

This is Beta 1. We expect some issues.

  • The reverser is nearly developed but not accessible in this api. This constructs expressions from AST trees. It is usefull for integration with UIX based expression builders.

  • We expect some issues with using expressions that uses js "includes"

📦 Template Structure

{
  "name": "__PACKAGE_NAME__",
  "type": "module",
  "version": "0.0.1",
  "private": true,
  "description": "__PACKAGE_DESCRIPTION__",
  "scripts": {
    "lint": "eslint --cache .",
    "lint:fix": "eslint . --fix",
    "release": "bumpp -r && pnpm -r publish",
    "test": "echo \"Error: no test specified\" && exit 1",
    "typecheck": "tsc --noEmit",
    "build": "tsc",
    "pub": "npm publish --access public"
  }
}

🗂️ File Structure

  • src/: Source files written in TypeScript.
  • dist/: Transpiled source files in JavaScript.
  • dist-test/: Transpiled source files in JavaScript in test mode.

🔗 Links

📄 License

Proprietary License © 2023 Ivar Strand

Readme

Keywords

Package Sidebar

Install

npm i @datadayrepos/js-lexer

Weekly Downloads

0

Version

0.0.1-beta.14

License

Proprietary

Unpacked Size

42.4 kB

Total Files

11

Last publish

Collaborators

  • ivstrand