Sentinel-IR 0.1.2:在 Android Termux 上安装、分析 JavaScript 并验证 AST 检测结果
无需电脑、无需 Root、无需 API Key:直接用 Android 手机运行 JavaScript 结构分析与回归测试。 Sentinel-IR 是一个用于 JavaScript 代码结构分析的开源工具。它可以从源代码中提取路由、模块导入、环境变量、文件系统操作、网络相关操作以及潜在风险信号,并将这些信息整理成紧凑的结构化报告。 对于 AI Agent 来说,这意味着不必在每一次分析中都直接传入整个源文件。系统可以先获得一份结构
无需电脑、无需 Root、无需 API Key:直接用 Android 手机运行 JavaScript 结构分析与回归测试。
Sentinel-IR 是一个用于 JavaScript 代码结构分析的开源工具。它可以从源代码中提取路由、模块导入、环境变量、文件系统操作、网络相关操作以及潜在风险信号,并将这些信息整理成紧凑的结构化报告。
对于 AI Agent 来说,这意味着不必在每一次分析中都直接传入整个源文件。系统可以先获得一份结构化的事实摘要,再根据任务决定是否需要查看原始代码。
本文将两个实践指南合并为一套完整流程:
- 从零开始在 Android Termux 中安装 Sentinel-IR。
- 初始化随 npm 包提供的 WebAssembly 解析器。
- 分析一个可复现的 JavaScript 示例。
- 检查 JSON 报告中的结构化事实。
- 使用 Sentinel-IR 0.1.2 验证动态代码执行、变量作用域和 "child_process" 检测。
- 运行针对特定回归场景的测试。
- 检查发布包,并在需要时运行完整开发测试套件。
- 了解分析器的限制,以及如何安全地分享测试结果。
项目链接
- npm 包:https://www.npmjs.com/package/sentinel-ir
- Sentinel-IR 0.1.2:https://www.npmjs.com/package/sentinel-ir/v/0.1.2
- GitLab 源代码:https://gitlab.com/jakubrojicek11/sentinel-ir
- 0.1.1 到 0.1.2 的代码变更:https://gitlab.com/jakubrojicek11/sentinel-ir/-/compare/v0.1.1...adf875a1502e3bcf7242bff85a32dcf62f023c07
一、开始之前:需要准备什么?
你需要:
- 一部能够运行 Termux 的 Android 手机。
- 可以访问软件仓库和 npm registry 的网络连接。
- 一些可用存储空间。
- 大约 20 至 30 分钟,具体时间取决于下载速度和设备环境。
不需要 Root 权限、桌面电脑、OpenAI 账户或 AI API Key。
安装 Termux
建议从官方渠道获取 Termux:
- F-Droid:https://f-droid.org/packages/com.termux/
- 官方 GitHub:https://github.com/termux/termux-app
不要随意安装聊天群或不明下载网站提供的 APK。
如果手机上已经安装了 Termux,不要为了本教程直接卸载。来自不同发行渠道的版本可能使用不同的签名密钥。
本文的验证原则
本文区分三种不同的检查:
- 安装检查:确认安装的是指定版本的 npm 包。
- 功能检查:确认解析器能处理给定的 JavaScript 示例,并输出预期事实。
- 回归测试:确认特定语法和作用域场景得到预期分类。
通过其中一种检查,不等于证明整个程序安全,也不等于证明分析器覆盖了所有 JavaScript 语法。
二、打开 Termux 并更新软件包
打开 Termux,等待终端提示符出现。
一次复制一个命令块,执行后等待命令结束,再继续下一步。
不要复制文档中的提示符 "$",只复制代码块里的命令。
第 1 步:更新 Termux
运行:
pkg update && pkg upgrade -y
这会更新软件包列表并升级已安装的软件包。
如果遇到仓库或镜像错误,先解决软件源问题,不要直接跳过。
第 2 步:安装 Node.js 和 npm
Sentinel-IR 要求 Node.js 20 或更新版本。
pkg install nodejs -y
检查版本:
node --version
npm --version
进一步检查 Node.js 主版本:
node -e "const major = Number(process.versions.node.split('.')[0]); console.log('Node.js:', process.version); if (major < 20) { console.error('Sentinel-IR requires Node.js 20 or newer'); process.exit(1); } console.log('PASS: Node.js version is supported');"
预期结果:
PASS: Node.js version is supported
如果 Node.js 版本低于 20,请先更新 Termux 软件包并检查可用版本。
三、创建独立测试目录
为了避免修改其他项目,我们先创建一个专门的测试目录。
mkdir -p "$HOME/sentinel-ir-termux-test"
cd "$HOME/sentinel-ir-termux-test"
pwd
输出路径通常以以下内容开头:
/data/data/com.termux/files/home/
后续测试文件都放在这个目录中。
以后可以使用以下命令返回:
cd "$HOME/sentinel-ir-termux-test"
四、安装指定版本 Sentinel-IR 0.1.2
本教程以 0.1.2 为主要验证版本。
首先创建一个最小 npm 项目:
npm init -y
安装指定版本:
npm install --save-exact [email protected]
检查实际安装的版本:
npm ls sentinel-ir
预期依赖树中包含:
然后检查公共 npm registry:
npm view [email protected] version
npm view sentinel-ir dist-tags --json
版本查询应返回:
0.1.2
发行标签中应包含:
{
"latest": "0.1.2"
}
这里有一个重要区别:本地 "package.json" 中写着某个版本,并不能证明该版本已经在公共 npm registry 中发布。registry 查询可以独立验证这一点。
如果 npm 无法访问 registry,请先检查网络连接:
npm ping
不要把网络超时直接当作分析器故障。
五、检查公开 API
运行以下命令,确认需要的函数已经导出:
node - <<'NODE'
const sentinel = require('sentinel-ir');
for (const name of ['analyzeAsync', 'compress', 'initializeWasm']) {
console.log(${name}: ${typeof sentinel[name]});
if (typeof sentinel[name] !== 'function') {
console.error(FAIL: missing function ${name});
process.exitCode = 1;
}
}
if (!process.exitCode) {
console.log('PASS: expected Sentinel-IR functions are exported');
}
NODE
预期结果应包含:
analyzeAsync: function
compress: function
initializeWasm: function
PASS: expected Sentinel-IR functions are exported
如果出现 "Cannot find module",请检查当前目录和安装状态:
cd "$HOME/sentinel-ir-termux-test"
npm ls sentinel-ir
如果安装的不是 0.1.2,请返回安装步骤。
六、初始化 WebAssembly 解析器
Sentinel-IR 包含 WebAssembly 解析路径。在 Android 等环境中,如果可选的原生 Tree-sitter 依赖无法正常构建,可以尝试使用该路径。
运行:
node - <<'NODE'
const { initializeWasm } = require('sentinel-ir');
initializeWasm()
.then(() => {
console.log('PASS: Sentinel-IR WebAssembly parser initialized');
})
.catch((error) => {
console.error('FAIL: WebAssembly initialization failed');
console.error(error);
process.exitCode = 1;
});
NODE
预期结果:
PASS: Sentinel-IR WebAssembly parser initialized
如果初始化失败,先不要继续假设解析器已经可以工作。
检查安装版本及相关依赖:
npm ls sentinel-ir web-tree-sitter
ls -lh node_modules/sentinel-ir/wasm/
检查包中是否包含 JavaScript 对应的 WebAssembly 语法文件:
tree-sitter-javascript.wasm
文件缺失、安装不完整或依赖初始化失败,都需要单独调查。
七、创建一个可复现的 JavaScript 示例
接下来创建一个简单的 Express 风格服务器代码。
注意:这里仅分析源代码,不会运行服务器,也不需要安装 Express。
创建文件:
cat > sample.js <<'JS'
const express = require("express");
const fs = require("fs");
const app = express();
app.post("/upload", (req, res) => {
fs.writeFileSync("/tmp/upload", req.body);
res.end();
});
app.listen(process.env.PORT);
JS
检查文件内容:
cat sample.js
检查语法:
node --check sample.js
如果语法正确,Node.js 通常不会输出任何内容,并会返回终端提示符。
这个示例刻意包含几个容易识别的结构:
- "POST /upload" 路由。
- "express" 和 "fs" 模块导入。
- "process.env.PORT" 环境变量。
- 文件系统写入操作。
这些结构可以用来检查分析器是否提取了预期事实。
八、创建分析脚本
下面的脚本读取指定 JavaScript 文件,将源代码传给 "analyzeAsync()",再通过 "compress()" 生成紧凑报告。
它不会执行输入文件,也不会调用 LLM 或上传源代码。
创建脚本:
cat > analyze.js <<'JS'
const fs = require('node:fs/promises');
const path = require('node:path');
const { analyzeAsync, compress } = require('sentinel-ir');
async function main() {
const inputPath = process.argv[2];
if (!inputPath) {
console.error('Usage: node analyze.js ');
process.exitCode = 1;
return;
}
const source = await fs.readFile(inputPath, 'utf8');
const ir = await analyzeAsync(source, {
target: path.basename(inputPath),
});
const report = compress(ir);
console.log(JSON.stringify(report, null, 2));
if (report.analysisStatus !== 'complete') {
console.error(
'WARNING: analysisStatus is not complete. Do not treat this result as a complete structural analysis.'
);
process.exitCode = 2;
}
}
main().catch((error) => {
console.error('ERROR: analysis could not finish.');
console.error(error && error.stack ? error.stack : error);
process.exitCode = 1;
});
JS
检查脚本自身的语法:
node --check analyze.js
这一步只验证包装脚本的语法,不验证 Sentinel-IR 的检测准确性。
九、运行分析并保存 JSON 报告
执行:
node analyze.js sample.js > report.json
rc=$?
printf 'Analyzer exit code: %s\n' "$rc"
查看报告:
cat report.json
退出码含义:
- "0":脚本生成了 "analysisStatus" 为 "complete" 的报告。
- "2":生成了报告,但分析状态不完整。
- "1":脚本无法完成分析,例如无法读取文件或初始化解析器失败。
对于本文提供的有效 JavaScript 示例,预期可以检查以下内容:
- "analysisStatus" 为 "complete"。
- "facts.ast" 为 "true"。
- "facts.routes" 包含 "POST /upload"。
- "facts.imports" 包含 "express" 和 "fs"。
- "facts.env" 包含 "PORT"。
- 报告中包含与路由和文件写入相关的操作或风险信号。
实际报告中的其他字段及其格式可能随输入和实现而变化。
还可以单独查看主要字段:
node - <<'NODE'
const r = require('./report.json');
console.log('analysisStatus:', r.analysisStatus);
console.log('routes:', r.facts.routes);
console.log('imports:', r.facts.imports);
console.log('environment variables:', r.facts.env);
console.log('operations:', r.facts.operations);
console.log('risk signals:', r.facts.riskSignals);
NODE
十、自动验证预期结果
仅仅看到一份 JSON 并不意味着分析正确。
我们可以编写一个小检查程序,对示例中的已知结构进行断言。
node - <<'NODE'
const report = require('./report.json');
const failures = [];
if (report.analysisStatus !== 'complete') {
failures.push(analysisStatus expected complete, got ${report.analysisStatus});
}
if (report.facts?.ast !== true) {
failures.push('facts.ast expected true');
}
if (!Array.isArray(report.facts?.routes) ||
!report.facts.routes.includes('POST /upload')) {
failures.push('expected route POST /upload was not found');
}
if (!Array.isArray(report.facts?.imports) ||
!report.facts.imports.includes('express')) {
failures.push('expected import express was not found');
}
if (!Array.isArray(report.facts?.imports) ||
!report.facts.imports.includes('fs')) {
failures.push('expected import fs was not found');
}
if (!Array.isArray(report.facts?.env) ||
!report.facts.env.includes('PORT')) {
failures.push('expected environment variable PORT was not found');
}
if (failures.length) {
console.error('FAIL: sample analysis did not match expectations');
for (const failure of failures) {
console.error(- ${failure});
}
process.exitCode = 1;
} else {
console.log('PASS: the sample produced the expected structural facts');
}
NODE
预期结果:
PASS: the sample produced the expected structural facts
这是一个可复现的 smoke test。它证明的是:在当前安装环境中,给定示例产生了检查程序所要求的结构化事实。
它不是完整的安全审计,不是全面的准确率基准测试,也不证明所有 JavaScript 模式都能被正确识别。
十一、尝试分析自己的 JavaScript 文件
完成已知示例的测试后,可以尝试分析自己拥有或有权检查的 JavaScript 文件。
选项 A:文件已经位于 Termux 中
将文件复制到测试目录,或者直接使用其路径。
node analyze.js ./your-file.js > your-file-report.json
rc=$?
printf 'Analyzer exit code: %s\n' "$rc"
将 "your-file.js" 替换成实际文件名。
请为报告使用不同的文件名,避免覆盖之前的 "report.json"。
选项 B:文件位于 Android Downloads
如果需要访问 Android 的共享存储,可以执行:
termux-setup-storage
按照 Android 显示的权限提示进行授权。
列出下载目录:
ls "$HOME/storage/downloads"
复制一个 JavaScript 文件:
cp "$HOME/storage/downloads/example.js" "$HOME/sentinel-ir-termux-test/your-file.js"
cd "$HOME/sentinel-ir-termux-test"
node analyze.js your-file.js > your-file-report.json
rc=$?
printf 'Analyzer exit code: %s\n' "$rc"
将 "example.js" 替换成实际文件名。
如果文件名包含空格,请保留命令中的引号。
重要限制
Sentinel-IR 当前面向 JavaScript,包括 CommonJS 和 ESM。
它不声称能够可靠地分析所有 TypeScript、复杂 JSX 或其他编程语言。
此外,分析器一次处理一个 JavaScript 文件,不会自动完成跨文件调用图或数据流分析。
十二、理解 analysisStatus
"analysisStatus" 是理解报告时最重要的字段之一。
complete
表示 AST 提取成功,源代码解析没有出现导致分析不完整的语法错误。
但这不代表已经发现所有安全问题,也不代表程序一定安全。
incomplete
表示 AST 提取不可用,或者源代码未能被完整解析。
此时输出可能依赖基于文本的回退启发式规则,结果可能出现误报或漏报。
如果报告不完整,不要声称 Sentinel-IR 已经确认该文件安全。
建议检查:
node --check sample.js
确认输入是受支持的 JavaScript,并尝试缩小问题示例。
即使状态为 "complete",仍然存在明确的分析边界:
- 这是基于语法结构的分析,不是完整语义分析。
- 一次只分析一个文件。
- 不一定能解析别名、动态计算的属性名或跨模块传递的行为。
- 不证明应用程序安全。
- 不能消除 Prompt Injection。
- 不会运行应用程序,也不会替 AI Agent 决定应该采取什么行动。
像 "world.confidence" 这样的字段,也不应被误解为经过统计校准的正确率概率。
第二部分:Sentinel-IR 0.1.2 的 AST 检测改进
十三、为什么 0.1.2 值得单独测试?
0.1.2 的重点不是重新设计整个 API,而是改善若干具体 JavaScript 语法场景中的结构化事实提取。
本次发布重点包括:
- 识别更多动态代码执行形式。
- 更好地区分全局 "eval"、"Function" 与同名局部变量。
- 识别部分使用字符串字面量的计算属性方法调用。
- 将静态 "child_process" 导入记录为一种潜在能力。
- 通过回归测试检查正确识别和正确不识别的情况。
在发布验证记录中,开发测试套件通过了 69 项测试,GitLab CI 的三个任务全部成功,独立消费者环境中的定向测试通过了 8 项。
这些结果支持特定实现和测试场景,不代表完整的 JavaScript 安全覆盖。
十四、变量名称相同,不代表行为相同
考虑以下代码:
eval(userInput);
以及:
parser.eval(userInput);
两者都包含 "eval",但不一定执行相同的操作。
第一种形式通常调用全局 "eval",除非该名称被局部绑定遮蔽。
第二种形式调用 "parser" 对象的方法。它可能只是解析表达式或验证字符串。
如果检测器仅仅搜索字符串 "eval",就容易产生误报。
类似地:
function evaluate(value) {
return value * 2;
}
module.exports = { evaluate };
这里的 "evaluate" 只是一个普通函数名称,并没有执行动态代码。
分析器应该识别实际语法结构,而不是把名称相似当作能力相同。
十五、识别 globalThis 的不同写法
JavaScript 可以通过 "globalThis" 访问全局对象。
以下代码表达了相关的全局操作:
eval(userInput);
globalThis.eval(userInput);
globalThis"eval";
"Function" 构造器也有类似形式:
new Function("return 1");
new globalThis.Function("return 1");
new globalThis"Function";
这些写法可能对应不同的 AST 节点。
0.1.2 扩展了相应的识别逻辑和测试,覆盖更多显式写出的动态代码执行形式。
它也考虑了 "globalThis" 自身被局部变量遮蔽的情况。如果源文件声明了一个名为 "globalThis" 的局部变量,分析器不能仅凭名称就认定它是真正的全局对象。
十六、区分局部变量与全局 Function
例如:
const Function = value => value;
Function("ordinary text");
这里的 "Function" 是局部变量,并不是全局 "Function" 构造器。
类似情况也可能发生在函数参数中:
function run(Function) {
return Function("ordinary text");
}
或者导入绑定中:
import Function from "safe-module";
Function("ordinary text");
如果检测器把所有名为 "Function" 的调用都标记为动态代码执行,就会把这些情况错误地分类。
0.1.2 在 "src/ast-facts.js" 中扩展了保守的 AST 作用域检查,用于识别局部变量、参数和导入绑定对全局名称的遮蔽。
相关辅助函数包括:
- "bindingPatternHasName()"
- "lexicalDeclarationHasName()"
- "importStatementHasBinding()"
- "lexicalScopeHasBinding()"
- "varScopeHasBinding()"
- "functionScopeHasBinding()"
- "isNameShadowed()"
- "isUnshadowedGlobalThis()"
这不是完整的 JavaScript 名称解析器,但它比简单搜索关键词更精确。
十七、识别计算属性调用
JavaScript 支持点号调用:
cp.execSync("example");
也支持方括号形式:
cp"execSync";
对于 AST 来说,这两种写法可能对应不同节点类型。
0.1.2 扩展了 "callTarget()" 对可识别字符串字面量属性的处理,使分析器可以在特定条件下将调用归一化为:
object: cp
property: execSync
如果 "cp" 已经与 "child_process" 模块关联,这种信息就能帮助分析器识别潜在的进程创建操作。
但以下形式更复杂:
cpmethodName;
如果 "methodName" 的值需要运行时才能确定,分析器不能自动假设它就是 "execSync"。
0.1.2 的改进针对可静态识别的属性,而不是承诺解析所有动态属性。
十八、导入 child_process 不等于执行系统命令
考虑:
import { execSync } from "node:child_process";
这段代码导入了一个能够启动系统命令的 API。
但是,导入本身并不能证明已经执行了命令。
真正的调用可能是:
execSync("some-command");
因此,需要区分两个事实:
- 文件导入了具有特定能力的模块。
- 文件中检测到了实际的进程创建调用。
0.1.2 将静态 "child_process" 导入作为能力信号记录,并将它与 "processSpawn" 分开。
例如,仅有导入的测试场景预期包含:
childProcess: true
processSpawn: false
这里有一个容易误解的地方:当前风险策略仍然可能因为存在 "child_process" 能力而将整体风险设为 "critical"。
这是一种保守的风险分类策略。
"critical" 并不证明文件已经执行了命令,也不证明代码具有恶意。
下游系统应该同时检查能力、实际操作及相关证据,而不是把整体风险标签当作完整的运行时行为描述。
十九、0.1.2 的主要变化
检测领域| 改进内容
动态代码执行| 覆盖更多全局 "eval" 和 "Function" 形式
计算属性| 识别部分字符串字面量方法调用
局部变量| 区分局部 "eval"、"Function" 与全局函数
函数参数| 增加参数遮蔽相关测试
globalThis| 检查全局对象名称被局部变量遮蔽的情况
对象与类方法| 避免把方法名称错误当作词法绑定
ES Module 导入| 为静态 "child_process" 导入添加能力信号
风险证据| 增加针对操作标志、风险等级和风险信号的回归测试
发布验证| 69/69 开发测试、通过的 CI,以及 8/8 定向消费者测试
这些改进的目标是让结构化事实更加准确,而不是让分析器声称自己了解所有程序行为。
第三部分:在 Termux 中复现 0.1.2 的回归测试
二十、创建独立的版本测试目录
前面的基础示例验证了路由、导入和环境变量。
现在,我们测试 0.1.2 特别关注的场景。
mkdir -p "$HOME/sentinel-ir-v012-check"
cd "$HOME/sentinel-ir-v012-check"
npm init -y
npm install --save-exact [email protected]
npm ls sentinel-ir
确认安装的是 "[email protected]"。
二十一、创建六个定向测试
以下测试会把源代码字符串交给分析器,而不是执行这些源代码。
创建文件:
cat > check-v012.js <<'NODE'
const assert = require("node:assert/strict");
const { analyzeAsync } = require("sentinel-ir");
const cases = [
{
name: "local Function is not the global constructor",
source: 'const Function = value => value; Function("ordinary text");',
risk: "low",
operations: { dynamicEval: false }
},
{
name: "global Function is dynamic code",
source: 'Function("return 1");',
risk: "critical",
operations: { dynamicEval: true }
},
{
name: "computed globalThis eval is detected",
source: 'globalThis"eval";',
risk: "critical",
operations: { dynamicEval: true }
},
{
name: "computed child_process method is detected",
source: 'const cp = require("child_process"); cp"execSync";',
risk: "critical",
operations: {
childProcess: true,
processSpawn: true
}
},
{
name: "ordinary object eval method is not global eval",
source: 'const parser = { eval(input) { return input; } }; parser.eval("text");',
risk: "low",
operations: { dynamicEval: false }
},
{
name: "static child_process import records capability, not a spawn",
source: 'import { execSync } from "node:child_process";',
risk: "critical",
operations: {
childProcess: true,
processSpawn: false
}
}
];
async function main() {
for (const item of cases) {
const result = await analyzeAsync(item.source, {
target: "smoke-test.js"
});
assert.equal(
result.analysisStatus,
"complete",
${item.name}: parsing should complete
);
assert.equal(
result.world.risk,
item.risk,
${item.name}: unexpected risk
);
for (const [operation, expected] of Object.entries(item.operations)) {
assert.equal(
result.facts.operations[operation],
expected,
${item.name}: operations.${operation}
);
}
console.log(PASS: ${item.name} (${result.world.risk}));
}
console.log(\n${cases.length}/${cases.length} smoke tests passed.);
}
main().catch(error => {
console.error(error);
process.exitCode = 1;
});
NODE
运行测试:
node check-v012.js
如果所有断言都通过,最后一行应为:
6/6 smoke tests passed.
这些测试检查:
- 名为 "Function" 的局部变量不会自动被认定为全局构造器。
- 真正的全局 "Function()" 会被识别为动态代码执行。
- "globalThis"eval"" 会被识别。
- 与 "child_process" 关联的 "cp"execSync"" 会被识别为进程创建操作。
- 普通的 "parser.eval()" 不会仅因名称而被认定为全局 "eval"。
- 静态导入 "child_process" 会记录能力,但不会凭空证明进程已经启动。
最后一项尤其重要:测试预期 "processSpawn" 为 "false",但整体风险仍可能为 "critical"。这是当前风险策略的预期行为,而不是测试矛盾。
如果测试失败,不要直接修改预期值来让它通过。先记录错误、Node.js 版本和安装的包版本。
二十二、检查完整结构化报告
如果你不仅想检查风险等级,还想查看完整的紧凑 IR,可以创建另一个文件:
cat > inspect-v012.js <<'NODE'
const { analyzeAsync, compress } = require("sentinel-ir");
const code = ;
const cp = require("child_process");
cp["execSync"]("example");
async function main() {
const result = await analyzeAsync(code, {
target: "example.js"
});
console.log(JSON.stringify(compress(result), null, 2));
}
main().catch(error => {
console.error(error);
process.exitCode = 1;
});
NODE
运行:
node inspect-v012.js
检查报告中的:
- "facts"
- "operations"
- "riskSignals"
- 与识别结果相关的证据
对于这个示例,预期可以看到与 "child_process" 相关的能力,以及被识别的进程创建操作。
具体行号和其他字段可能随输入变化。
重点是:检测结果应当来自解析后的代码结构,而不只是注释或字符串中出现了某个关键词。
二十三、在需要时运行完整开发测试套件
npm 发布包主要包含运行时文件和随包提供的 WebAssembly 语法文件,并不包含完整的开发测试套件。
如果你想检查源代码中的完整测试,需要单独克隆仓库。
cd "$HOME"
git clone https://gitlab.com/jakubrojicek11/sentinel-ir.git
cd sentinel-ir
git checkout adf875a1502e3bcf7242bff85a32dcf62f023c07
npm ci
npm test
这里固定了对应 0.1.2 发布的提交,以避免把其他版本的代码误当作本次发布内容。
该发布版本的开发测试结果为:
69 tests passed
0 tests failed
需要注意,Android 上的可选原生 Tree-sitter 依赖可能与桌面 Linux 环境不同。
如果原生解析器无法初始化,前面针对已发布 npm 包的测试使用的是 "analyzeAsync()" 和 WebAssembly 路径。
不要把原生依赖安装失败与所有分析功能都不可用混为一谈,也不要在没有验证的情况下宣称所有 Android 设备都能顺利运行。
二十四、不同验证结果分别证明什么?
本次发布的验证记录包括以下几类检查:
- 本地开发测试
- 69 项测试通过。
- GitLab CI
- Pipeline:"#2964050242" (https://gitlab.com/jakubrojicek11/sentinel-ir/-/pipelines/2964050242)
- 对应提交:"adf875a1"
- 三个 CI 任务全部成功。
- 独立消费者测试
发布包 "sentinel-ir-0.1.2.tgz" 被安装到独立消费者目录中。
8 项定向测试通过,涉及变量遮蔽、导入绑定、方法名称、全局动态代码执行及 "globalThis" 访问。
- 公共 npm registry
通过以下命令检查实际发布状态:
npm view [email protected] version
npm view sentinel-ir dist-tags --json
这些检查回答的是不同的问题,不能相互替代。
本地测试通过,不代表公共包一定正确发布;CI 通过,也不代表每一种 Android 设备环境都已经被验证。
第四部分:常见问题与故障排查
二十五、Termux 软件仓库错误
如果执行 "pkg update" 时出现仓库不可用或镜像错误,可以运行:
termux-change-repo
在界面中选择可用的官方镜像,然后再次运行:
pkg update && pkg upgrade -y
请优先使用 Termux 官方文档中的仓库和镜像说明,不要因为某个镜像超时就复制来源不明的安装命令。
二十六、出现 node: command not found
重新检查软件包安装:
pkg update
pkg install nodejs -y
node --version
npm --version
如果软件包管理器本身无法正常工作,请先解决仓库问题。
二十七、Node.js 版本低于 20
运行:
pkg update && pkg upgrade -y
pkg install nodejs -y
node --version
如果版本仍然低于 20,请检查当前 Termux 仓库提供的版本。
不要盲目安装多个互相冲突的 Node.js 软件包。
二十八、npm install 出现原生 Tree-sitter 编译错误
Sentinel-IR 的原生 Tree-sitter 依赖是可选的。
如果原生依赖无法构建,可以在专门的测试目录中尝试跳过可选依赖:
cd "$HOME/sentinel-ir-termux-test"
rm -rf node_modules package-lock.json
npm install --save-exact --omit=optional [email protected]
这会跳过可选的原生解析器依赖,但不会跳过 WebAssembly 路径所需的常规 "web-tree-sitter" 依赖。
安装后重新检查:
npm ls sentinel-ir web-tree-sitter
然后重复 API 检查和 WebAssembly 初始化步骤。
注意:只在本教程创建的专用测试目录中执行清理命令。 不要在需要保留依赖的其他项目中随意删除 "node_modules"。
二十九、Cannot find module 'sentinel-ir'
检查当前目录和依赖:
cd "$HOME/sentinel-ir-termux-test"
npm ls sentinel-ir
如果没有安装,再运行:
npm install --save-exact [email protected]
如果仍然失败,请保留完整错误信息。
三十、WebAssembly 初始化失败
检查安装的版本和文件:
cd "$HOME/sentinel-ir-termux-test"
npm ls sentinel-ir web-tree-sitter
ls -lh node_modules/sentinel-ir/wasm/
确认包中包含 "tree-sitter-javascript.wasm"。
如果文件缺失或安装不完整,可以在专用测试目录中重新安装指定版本。
不要从不明网站下载其他 WebAssembly 二进制文件来替换它。
如果问题仍然存在,请记录 Node.js 版本、包版本和完整错误信息。
三十一、analysisStatus 为 incomplete
首先检查输入文件语法:
node --check sample.js
确认输入是受支持的 JavaScript,而不是超出当前支持范围的 TypeScript 或复杂 JSX。
不要将基于回退启发式规则生成的结果当作完整 AST 分析的等价结果。
三十二、预期路由或导入没有被识别
先确认分析的确实是教程中的示例:
cat sample.js
node analyze.js sample.js > report.json
然后检查:
cat report.json
如果示例与教程完全一致、分析状态为 "complete",但预期事实仍然缺失,请保留经过清理的最小示例及报告。
记录包版本、Node.js 版本、实际输出和预期输出。这些信息可以构成一个可复现的问题报告。
不要为了让测试通过而随意修改预期结果。
三十三、npm 无法访问 registry
先检查网络:
npm ping
npm view [email protected] version
如果 registry 暂时不可用,可以稍后重试,或者切换 Wi-Fi 和移动网络。
在 registry 请求本身无法成功的情况下,不应直接将安装超时归因于分析器代码。
第五部分:安全地分享测试结果
三十四、不要直接公开完整项目报告
Sentinel-IR 的 JSON 报告可能包含:
- 导入的模块名称。
- 路由路径。
- 环境变量名称。
- 操作类型。
- 风险信号。
- 目标文件名。
这些信息可能暴露项目的内部结构。
即使报告没有主动提取秘密值,也不能假设报告天然适合公开。
在发布结果之前:
- 检查报告中的每个字段。
- 删除或泛化内部路由、文件名、客户信息和专有架构信息。
- 不要未经许可发布他人的源代码或报告。
- 优先使用本文提供的合成示例进行演示。
- 明确说明测试的是合成示例还是自己有权检查的项目。
- 记录准确的版本、Node.js 版本、命令、预期结果和实际结果。
- 删除访问令牌、个人文件路径和其他敏感信息。
三十五、可复现测试报告模板
你可以将下面的模板用于 DEV Community、GitLab 讨论区或其他技术社区。
Title: Sentinel-IR 0.1.2 Termux smoke test
Device/OS: [Android version and device, if you want to share]
Termux source: [F-Droid or official GitHub]
Node.js: [output of node --version]
Sentinel-IR: 0.1.2
Input: [synthetic sample / my own authorized JavaScript file]
Command: node analyze.js sample.js
Expected:
analysisStatus=complete
route POST /upload
imports express and fs
environment variable PORT
Actual: [what really happened]
Result: [PASS / FAIL / INCOMPLETE]
Notes: [reproducible error or unexpected field, without secrets]
使用你自己的 DEV Community 或 GitLab 账户发布测试结果即可,不需要访问维护者账户。
一份有价值的问题报告应包含最小可公开示例、准确版本、预期行为、实际行为,以及分析状态是否完整。
最终检查清单
在结束之前,请确认以下事项。
- [ ] "node --version" 显示 Node.js 20 或更新版本。
- [ ] "npm ls sentinel-ir" 显示 "[email protected]"。
- [ ] "analyzeAsync"、"compress" 和 "initializeWasm" 已正确导出。
- [ ] WebAssembly 初始化成功。
- [ ] 示例文件通过语法检查。
- [ ] "node analyze.js sample.js" 成功生成报告。
- [ ] 报告的 "analysisStatus" 为 "complete"。
- [ ] 路由、导入和环境变量检查通过。
- [ ] 六项 0.1.2 定向 smoke tests 全部通过。
- [ ] 了解 "critical" 风险等级不等于已证明恶意或已执行命令。
- [ ] 了解 smoke test 不是完整安全认证。
如果某一步失败,不要隐藏失败结果。
记录错误、版本和环境信息。一个其他开发者可以复现的失败结果,通常比一句没有细节的“工具无法运行”更有价值。
最后的邀请
Sentinel-IR 的目标不是声称分析器可以理解所有代码,而是在分析开始之前,为下一步决策提供更紧凑、更容易检查的结构化事实。
如果你使用 Android 和 Termux 完成了测试,欢迎分享:
- 哪些结构被正确识别?
- 哪些事实缺失?
- 是否出现误报?
- 不同 Node.js 版本或 Android 环境是否产生了不同结果?
请尽量提供可复现的最小示例,而不是只发布截图。
让代码分析的结论可以被检查,让工具的限制也保持可见。
项目地址:
- npm:https://www.npmjs.com/package/sentinel-ir/v/0.1.2
- GitLab:https://gitlab.com/jakubrojicek11/sentinel-ir
- 0.1.2 变更记录:https://gitlab.com/jakubrojicek11/sentinel-ir/-/compare/v0.1.1...adf875a1502e3bcf7242bff85a32dcf62f023c07
Sentinel-IR 0.1.2:在 Android Termux 上安装、分析并验证。
Originally published by Dev.to AI. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.
