luasec 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +20 -0
  2. package/bin/luasec.js +88 -0
  3. package/package.json +13 -0
package/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # luasec (npm launcher)
2
+
3
+ luasec is a static security scanner for Lua in embedded firmware: it finds
4
+ remote code execution without installing Lua tooling.
5
+
6
+ ```sh
7
+ npx luasec <path>
8
+ ```
9
+
10
+ Requirements: macOS or Linux; Lua 5.3+ on PATH, or `make` plus a C compiler
11
+ so the launcher can build the bundled Lua once.
12
+
13
+ The launcher downloads the release tarball matching this package's version
14
+ once and extracts it into `$LUASEC_CACHE` (else `$XDG_CACHE_HOME/luasec`,
15
+ else `~/.cache/luasec`), then runs the cached `bin/luasec` with your
16
+ arguments. Set `LUASEC_TARBALL=<path>` to install from a local tarball
17
+ instead of downloading (tests, offline installs) and `LUASEC_CACHE=<dir>`
18
+ to choose the cache directory.
19
+
20
+ See the [main README](https://github.com/Vaibhav91one/luasec#readme) for scanner usage.
package/bin/luasec.js ADDED
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+ // npx luasec: fetch the luasec release that matches this package's version into
4
+ // a cache directory once, then run it. Nothing but Node built-ins, the system
5
+ // tar, and (only when no Lua 5.3+ is on PATH) make and a C compiler.
6
+ const { spawnSync } = require('child_process');
7
+ const fs = require('fs');
8
+ const https = require('https');
9
+ const os = require('os');
10
+ const path = require('path');
11
+
12
+ const { version } = require('../package.json');
13
+ const name = `luasec-${version}`;
14
+ const url = `https://github.com/Vaibhav91one/luasec/releases/download/v${version}/${name}.tar.gz`;
15
+ const cache = process.env.LUASEC_CACHE
16
+ || path.join(process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache'), 'luasec');
17
+ const root = path.join(cache, name);
18
+ const entry = path.join(root, 'bin', 'luasec');
19
+
20
+ function fail(message) {
21
+ process.stderr.write(`luasec (npm): ${message}\n`);
22
+ process.exit(2);
23
+ }
24
+
25
+ function download(from, to, redirects = 5) {
26
+ return new Promise((resolve, reject) => {
27
+ https.get(from, (res) => {
28
+ if ([301, 302, 303, 307, 308].includes(res.statusCode) && res.headers.location && redirects > 0) {
29
+ res.resume();
30
+ resolve(download(res.headers.location, to, redirects - 1));
31
+ return;
32
+ }
33
+ if (res.statusCode !== 200) {
34
+ res.resume();
35
+ reject(new Error(`GET ${from}: HTTP ${res.statusCode}`));
36
+ return;
37
+ }
38
+ const out = fs.createWriteStream(to);
39
+ res.pipe(out);
40
+ out.on('finish', () => out.close(resolve));
41
+ out.on('error', reject);
42
+ }).on('error', reject);
43
+ });
44
+ }
45
+
46
+ function hasLua() {
47
+ const probe = spawnSync('lua', ['-e', 'os.exit(tonumber(_VERSION:match("%d+%.%d+")) >= 5.3 and 0 or 1)']);
48
+ return probe.status === 0;
49
+ }
50
+
51
+ async function install() {
52
+ fs.mkdirSync(cache, { recursive: true });
53
+ const staging = fs.mkdtempSync(path.join(cache, '.staging-'));
54
+ const tarball = process.env.LUASEC_TARBALL || path.join(staging, `${name}.tar.gz`);
55
+ if (!process.env.LUASEC_TARBALL) {
56
+ process.stderr.write(`luasec (npm): downloading ${url}\n`);
57
+ await download(url, tarball);
58
+ }
59
+ const untar = spawnSync('tar', ['xzf', tarball, '-C', staging], { stdio: 'inherit' });
60
+ if (untar.status !== 0) fail(`cannot extract ${tarball}`);
61
+ if (!fs.existsSync(path.join(staging, name, 'bin', 'luasec'))) fail(`${tarball} has no ${name}/bin/luasec`);
62
+ if (!hasLua()) {
63
+ process.stderr.write('luasec (npm): no Lua 5.3+ on PATH; building Lua once with make (needs a C compiler)\n');
64
+ const built = spawnSync('make', ['lua'], { cwd: path.join(staging, name), stdio: ['ignore', 2, 2] });
65
+ if (built.status !== 0) fail('building Lua failed; install Lua 5.3+ or a C compiler and try again');
66
+ }
67
+ try {
68
+ fs.renameSync(path.join(staging, name), root);
69
+ } catch (error) {
70
+ // Another first run finished installing the same version while this one
71
+ // was extracting: use its copy.
72
+ if (!fs.existsSync(entry)) throw error;
73
+ }
74
+ fs.rmSync(staging, { recursive: true, force: true });
75
+ }
76
+
77
+ (async () => {
78
+ if (!fs.existsSync(entry)) {
79
+ try {
80
+ await install();
81
+ } catch (error) {
82
+ fail(error.message);
83
+ }
84
+ }
85
+ const result = spawnSync(entry, process.argv.slice(2), { stdio: 'inherit' });
86
+ if (result.error) fail(result.error.message);
87
+ process.exit(result.status === null ? 2 : result.status);
88
+ })();
package/package.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "luasec",
3
+ "version": "0.2.0",
4
+ "description": "Static security scanner for Lua in embedded firmware: finds remote code execution. Runs luasec without installing Lua tooling.",
5
+ "bin": { "luasec": "bin/luasec.js" },
6
+ "files": ["bin/luasec.js", "README.md"],
7
+ "engines": { "node": ">=18" },
8
+ "os": ["darwin", "linux"],
9
+ "repository": { "type": "git", "url": "git+https://github.com/Vaibhav91one/luasec.git" },
10
+ "homepage": "https://github.com/Vaibhav91one/luasec",
11
+ "license": "MIT",
12
+ "keywords": ["lua", "security", "static-analysis", "firmware", "sarif", "openwrt"]
13
+ }