up
This commit is contained in:
31
plugins/scanner/node/manifest.json
Normal file
31
plugins/scanner/node/manifest.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"schemaVersion": "1.0",
|
||||
"id": "stellaops.analyzer.lang.node",
|
||||
"displayName": "StellaOps Node Analyzer",
|
||||
"version": "0.1.0",
|
||||
"requiresRestart": true,
|
||||
"entryPoint": {
|
||||
"type": "dotnet",
|
||||
"assembly": "StellaOps.Scanner.Analyzers.Lang.Node.dll",
|
||||
"typeName": "StellaOps.Scanner.Analyzers.Lang.Node.NodeAnalyzerPlugin"
|
||||
},
|
||||
"capabilities": [
|
||||
"language-analyzer",
|
||||
"node",
|
||||
"npm",
|
||||
"pnpm",
|
||||
"pnp",
|
||||
"runtime-optional"
|
||||
],
|
||||
"hooks": {
|
||||
"runtime": {
|
||||
"cjs": "runtime-hooks/runtime-require-hook.js",
|
||||
"esm": "runtime-hooks/runtime-esm-loader.mjs"
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"org.stellaops.analyzer.language": "node",
|
||||
"org.stellaops.analyzer.kind": "language",
|
||||
"org.stellaops.restart.required": "true"
|
||||
}
|
||||
}
|
||||
61
plugins/scanner/node/runtime-hooks/runtime-esm-loader.mjs
Normal file
61
plugins/scanner/node/runtime-hooks/runtime-esm-loader.mjs
Normal file
@@ -0,0 +1,61 @@
|
||||
// Runtime ESM loader for StellaOps Scanner runtime evidence
|
||||
// Usage: node --experimental-loader=./runtime-esm-loader.mjs app.mjs
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import crypto from 'crypto';
|
||||
import { fileURLToPath, pathToFileURL } from 'url';
|
||||
|
||||
const outPath = process.env.SCANNER_NODE_RUNTIME_OUT || path.join(process.cwd(), 'node-runtime-evidence.ndjson');
|
||||
const root = process.env.SCANNER_NODE_ROOT || process.cwd();
|
||||
const loaderId = hashLoaderId(import.meta.url);
|
||||
|
||||
function hashLoaderId(value) {
|
||||
return crypto.createHash('sha256').update(value || '').digest('hex');
|
||||
}
|
||||
|
||||
function scrub(p) {
|
||||
if (!p) return p;
|
||||
try {
|
||||
const absolute = p.startsWith('file:') ? fileURLToPath(p) : p;
|
||||
const rel = path.relative(root, absolute);
|
||||
return rel.startsWith('..') ? p : rel.split(path.sep).join('/');
|
||||
} catch {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
function emit(record) {
|
||||
try {
|
||||
fs.appendFileSync(outPath, JSON.stringify(record) + '\n');
|
||||
} catch {
|
||||
// best-effort: ignore write failures
|
||||
}
|
||||
}
|
||||
|
||||
export async function resolve(specifier, context, next) {
|
||||
const parent = context.parentURL ? scrub(context.parentURL) : undefined;
|
||||
const target = scrub(specifier);
|
||||
|
||||
emit({
|
||||
type: 'edge',
|
||||
from: parent,
|
||||
to: target,
|
||||
reason: 'runtime-import',
|
||||
loaderId
|
||||
});
|
||||
|
||||
return next(specifier, context, next);
|
||||
}
|
||||
|
||||
export async function load(url, context, next) {
|
||||
const pathOrUrl = scrub(url);
|
||||
emit({
|
||||
type: 'component',
|
||||
path: pathOrUrl,
|
||||
reason: 'runtime-load',
|
||||
loaderId
|
||||
});
|
||||
|
||||
return next(url, context, next);
|
||||
}
|
||||
49
plugins/scanner/node/runtime-hooks/runtime-require-hook.js
Normal file
49
plugins/scanner/node/runtime-hooks/runtime-require-hook.js
Normal file
@@ -0,0 +1,49 @@
|
||||
// Runtime require hook for StellaOps Scanner runtime evidence
|
||||
// Usage: node -r ./runtime-require-hook.js app.js
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const crypto = require('crypto');
|
||||
|
||||
const outPath = process.env.SCANNER_NODE_RUNTIME_OUT || path.join(process.cwd(), 'node-runtime-evidence.ndjson');
|
||||
const root = process.env.SCANNER_NODE_ROOT || process.cwd();
|
||||
|
||||
function hashLoaderId(value) {
|
||||
return crypto.createHash('sha256').update(value || '').digest('hex');
|
||||
}
|
||||
|
||||
function scrub(p) {
|
||||
if (!p) return p;
|
||||
try {
|
||||
const rel = path.relative(root, p);
|
||||
return rel.startsWith('..') ? p : rel.split(path.sep).join('/');
|
||||
} catch {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
function emit(record) {
|
||||
try {
|
||||
fs.appendFileSync(outPath, JSON.stringify(record) + '\n');
|
||||
} catch {
|
||||
// best-effort: ignore write failures
|
||||
}
|
||||
}
|
||||
|
||||
const originalLoad = module.constructor._load;
|
||||
module.constructor._load = function (request, parent, isMain) {
|
||||
const from = parent && parent.filename ? scrub(parent.filename) : undefined;
|
||||
const to = scrub(request);
|
||||
const loaderId = hashLoaderId(__filename);
|
||||
|
||||
emit({
|
||||
type: 'edge',
|
||||
from,
|
||||
to,
|
||||
reason: 'runtime-require',
|
||||
loaderId,
|
||||
isMain: !!isMain
|
||||
});
|
||||
|
||||
return originalLoad.apply(this, arguments);
|
||||
};
|
||||
Reference in New Issue
Block a user