Skip to content

ESLint rules for Manifest V3 extension safety. Enforces statically analyzable and self-contained code for cross-world script injections.

License

Notifications You must be signed in to change notification settings

mertcreates/eslint-plugin-mv3

Repository files navigation

eslint-plugin-mv3

npm version npm downloads license CI

ESLint rule(s) for MV3-safe scripting.executeScript usage.

It enforces that injected func code is self-contained, statically analyzable, and safe across Main World boundaries.

Contents

The Problem

When using Manifest V3 scripting.executeScript({ func }), the injected function is serialized and executed in Main World. Outer-scope values are not carried with it.

If injected code references variables outside its own scope, it can fail at runtime with ReferenceError: ... is not defined. This plugin catches those closure traps statically at lint time.

Features

  • Detects closure capture inside executeScript({ func })
  • Rejects imported/non-local func references
  • Enforces args: [...] when injected functions have parameters
  • Rejects dynamic/spread options that break static enforcement
  • Supports chrome and browser hosts
  • Supports alias/destructure/computed access, .call, .apply, .bind, Reflect.apply

Install

npm i -D @mertcreates/eslint-plugin-mv3
# or
yarn add -D @mertcreates/eslint-plugin-mv3
# or
pnpm add -D @mertcreates/eslint-plugin-mv3
# or
bun add -D @mertcreates/eslint-plugin-mv3

Usage (eslintrc)

{
  "plugins": ["@mertcreates/mv3"],
  "rules": {
    "@mertcreates/mv3/no-execute-script-closure": "error"
  }
}

Or use recommended config:

{
  "extends": ["plugin:@mertcreates/mv3/recommended"]
}

Usage (flat config)

import mv3Plugin from '@mertcreates/eslint-plugin-mv3';

export default [
  mv3Plugin.configs.recommended,
];

Rules

The recommended config enables this rule.

@mertcreates/mv3/no-execute-script-closure

Validates that:

  • func is local and resolvable in the same file
  • func does not capture outer-scope variables
  • args is present and array-literal when function parameters exist
  • invocation/config shape stays statically analyzable

Incorrect / Correct by covered case

  1. Closure capture (outer scope)

Incorrect:

const TOP = 'outer';

function installBridge() {
  return TOP;
}

chrome.scripting.executeScript({
  target: { tabId: 1 },
  func: installBridge,
});

Correct:

function installBridge(source) {
  return source;
}

chrome.scripting.executeScript({
  target: { tabId: 1 },
  func: installBridge,
  args: ['outer'],
});
  1. Imported func

Incorrect:

import { installBridge } from './bridge';

chrome.scripting.executeScript({
  target: { tabId: 1 },
  func: installBridge,
});

Correct:

function installBridge(source) {
  return source;
}

chrome.scripting.executeScript({
  target: { tabId: 1 },
  func: installBridge,
  args: ['ok'],
});
  1. Params exist but args missing/invalid

Incorrect:

function installBridge(config) {
  return config;
}

chrome.scripting.executeScript({
  target: { tabId: 1 },
  func: installBridge,
});

Correct:

function installBridge(config) {
  return config;
}

chrome.scripting.executeScript({
  target: { tabId: 1 },
  func: installBridge,
  args: [{ source: 'mv3' }],
});
  1. Dynamic/spread config

Incorrect:

const baseOptions = { target: { tabId: 1 } };

chrome.scripting.executeScript({
  ...baseOptions,
  func: () => Date.now(),
});

Correct:

chrome.scripting.executeScript({
  target: { tabId: 1 },
  func: () => Date.now(),
});

Options

No rule options right now. The rule is intentionally strict and zero-config.

Compatibility

  • ESLint: >=8.50.0 <10
  • Node: versions supported by your ESLint runtime

Benchmarks

Benchmarks separate:

  • ESLint core overhead
  • Rule-on cost
  • Net rule cost (rule-on - overhead)

Latest run highlights (BENCH_SCALE=1 BENCH_WARMUP=2 BENCH_RUNS=5):

  • noise-baseline-5k: net median rule cost ~2.96ms
  • mixed-worst-case (30k lines): net median rule cost ~16.19ms

See details in BENCHMARK.md.

License

MIT.

About

ESLint rules for Manifest V3 extension safety. Enforces statically analyzable and self-contained code for cross-world script injections.

Topics

Resources

License

Stars

Watchers

Forks