Files
qinglong/test/front/diff-file-routing.test.cjs
T
whyour 2cab112391 fix: 加固安全输入边界并修复订阅兼容性 (#3084)
* fix: harden scoped API inputs and isolate login throttling

* fix: close script path gaps and harden security boundaries

* fix: preserve subscription aliases and avoid extension eval

* fix: allow cancelling invalid historical subscriptions

* fix: make script parent validation explicit for CodeQL

* fix: limit command execution and clarify trusted path branches

* fix: separate diff tool script access from config APIs

* fix: clean up obsolete subscription SSH credentials
2026-10-06 18:21:54 +08:00

127 lines
3.5 KiB
JavaScript

const assert = require('node:assert/strict');
const test = require('node:test');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const ts = require('typescript');
const source = fs.readFileSync(
path.join(__dirname, '../../src/pages/diff/index.tsx'),
'utf8',
);
const ast = ts.createSourceFile(
'diff.tsx',
source,
ts.ScriptTarget.Latest,
true,
ts.ScriptKind.TSX,
);
const callbacks = new Map();
function visit(node) {
if (
ts.isVariableDeclaration(node) &&
['getConfig', 'updateConfig', 'getSample'].includes(node.name.getText(ast))
) {
callbacks.set(node.name.getText(ast), node.initializer.getText(ast));
}
ts.forEachChild(node, visit);
}
visit(ast);
function fixture(current, editorContent) {
const calls = [];
const values = {};
const context = {
current,
origin: 'sample/notify.js',
currentValue: '',
editorRef: {
current:
editorContent === undefined
? null
: {
getModel: () => ({ modified: { getValue: () => editorContent } }),
},
},
config: { apiPrefix: '/panel/api/' },
request: {
get: async (url) => {
calls.push({ method: 'GET', url });
return { code: 200, data: 'file content' };
},
post: async (url, body) => {
calls.push({ method: 'POST', url, body });
return { code: 200 };
},
},
setCurrentValue: (value) => {
values.current = value;
},
setOriginValue: (value) => {
values.origin = value;
},
message: {
success: (value) => {
values.message = value;
},
},
intl: { get: (value) => value },
};
vm.createContext(context);
for (const [name, initializer] of callbacks) {
vm.runInContext(
ts.transpile(`var ${name} = ${initializer}`, {
target: ts.ScriptTarget.ES2020,
}),
context,
);
}
return { context, calls, values };
}
for (const [target, filename] of [
['config.sh', null],
['data/scripts/sendNotify.js', 'sendNotify.js'],
['data/scripts/notify.py', 'notify.py'],
['data/scripts/子目录/a b.js', '子目录/a b.js'],
]) {
test(`diff tool reads and saves ${target} through its resource API`, async () => {
const { context, calls, values } = fixture(target, 'updated\r\n');
context.getConfig();
context.updateConfig();
await new Promise((resolve) => setImmediate(resolve));
assert.equal(
calls[0].url,
filename
? `/panel/api/scripts/detail?file=${encodeURIComponent(filename)}`
: '/panel/api/configs/detail?path=config.sh',
);
assert.equal(
calls[1].url,
filename ? '/panel/api/scripts' : '/panel/api/configs/save',
);
assert.equal(
calls[1].body[filename ? 'filename' : 'name'],
filename || target,
);
assert.equal(calls[1].body.content, 'updated\n');
assert.equal(values.current, 'file content');
assert.equal(values.message, '保存成功');
});
}
test('mobile diff tool can save an empty notification script and load its sample', async () => {
const { context, calls, values } = fixture('data/scripts/sendNotify.js');
context.updateConfig();
context.getSample();
await new Promise((resolve) => setImmediate(resolve));
assert.equal(calls[0].url, '/panel/api/scripts');
assert.equal(calls[0].body.filename, 'sendNotify.js');
assert.equal(calls[0].body.content, '');
assert.equal(
calls[1].url,
'/panel/api/configs/detail?path=sample%2Fnotify.js',
);
assert.equal(values.origin, 'file content');
});