fix(i18n): retain Python dependency and notification audit translations

This commit is contained in:
whyour
2026-10-14 10:25:13 +08:00
17 changed files with 524 additions and 142 deletions
@@ -0,0 +1,63 @@
const test = require('node:test'),
assert = require('node:assert/strict'),
fs = require('node:fs'),
os = require('node:os'),
path = require('node:path'),
{ execFileSync } = require('node:child_process');
const load = require('../helpers/load-security-module.cjs');
test('Python source arguments round-trip literally and local files stay inside maintained roots', (t) => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ql-pip-source-'));
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
const scripts = path.join(root, 'scripts'),
repo = path.join(root, 'repo');
fs.mkdirSync(scripts);
fs.mkdirSync(repo);
fs.mkdirSync(path.join(scripts, 'project space'));
const requirement = path.join(scripts, 'project space', 'requirements.txt');
fs.writeFileSync(requirement, 'requests');
fs.writeFileSync(
path.join(scripts, 'project space', 'pyproject.toml'),
'[project]\nname="sample"',
);
const outside = path.join(root, 'requirements.txt');
fs.writeFileSync(outside, 'outside');
fs.symlinkSync(outside, path.join(scripts, 'requirements-link.txt'));
const api = load('back/config/util.ts', {
'./index': { scriptPath: scripts, repoPath: repo },
'./const': { PYTHON_INSTALL_DIR: '/prefix with spaces' },
'../loaders/logger': {},
'../shared/utils': {},
'../data/dependence': {
DependenceTypes: { nodejs: 0, python3: 1, linux: 2 },
},
});
function args(cmd) {
return execFileSync('/bin/bash', [
'-c',
'pip3(){ printf "%s\\0" "$@"; }; ' + cmd,
])
.toString()
.split('\0')
.slice(0, -1);
}
const req = args(api.getInstallCommand(1, 'project space/requirements.txt'));
assert.equal(req.at(-1), requirement);
assert.equal(req.at(-2), '-r');
assert.ok(req.includes('--prefix=/prefix with spaces'));
assert.equal(
args(api.getInstallCommand(1, 'project space/pyproject.toml')).at(-1),
path.dirname(requirement),
);
const url = 'git+https://example.invalid/repo.git#egg=pkg;$(echo INJECTED)';
assert.equal(args(api.getInstallCommand(1, url)).at(-1), url);
assert.throws(() => api.getInstallCommand(1, '../requirements.txt'));
assert.throws(() => api.getInstallCommand(1, 'requirements-link.txt'));
assert.equal(
args(api.getUninstallCommand(1, 'project space/requirements.txt')).at(-1),
requirement,
);
assert.match(
api.getUninstallCommand(1, url),
/Remove installed Python distributions separately/,
);
});
+70
View File
@@ -0,0 +1,70 @@
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const os = require('node:os');
const path = require('node:path');
const { execFileSync } = require('node:child_process');
const load = require('../helpers/load-security-module.cjs');
test('requirements install, probe and removal stay in the selected venv', (t) => {
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'ql-source-venv-'));
t.after(() => fs.rmSync(root, { recursive: true, force: true }));
const venv = path.join(root, 'virtual env');
const scripts = path.join(root, 'scripts');
fs.mkdirSync(scripts);
execFileSync('python3', ['-m', 'venv', venv]);
const wheel = path.join(scripts, 'ql_venv_fixture-1.0-py3-none-any.whl');
execFileSync('python3', [
'-c',
`import zipfile,sys
with zipfile.ZipFile(sys.argv[1],'w') as z:
z.writestr('ql_venv_fixture.py','value = 42')
z.writestr('ql_venv_fixture-1.0.dist-info/METADATA','Metadata-Version: 2.1\\nName: ql-venv-fixture\\nVersion: 1.0\\n')
z.writestr('ql_venv_fixture-1.0.dist-info/WHEEL','Wheel-Version: 1.0\\nGenerator: fixture\\nRoot-Is-Purelib: true\\nTag: py3-none-any\\n')
z.writestr('ql_venv_fixture-1.0.dist-info/RECORD','')`,
wheel,
]);
fs.writeFileSync(path.join(scripts, 'requirements.txt'), wheel + '\n');
const api = load('back/config/util.ts', {
'./index': {
rootPath: root,
scriptPath: scripts,
repoPath: path.join(root, 'repo'),
},
'./const': {
PYTHON_VENV_DIR: 'virtual env',
PYTHON_INSTALL_DIR: '/unused-global-prefix',
},
'../loaders/logger': {},
'../shared/utils': {},
'../data/dependence': {
DependenceTypes: { nodejs: 0, python3: 1, linux: 2 },
},
});
const run = (command) =>
execFileSync('/bin/bash', ['-c', command], {
encoding: 'utf8',
env: {
...process.env,
PIP_NO_INDEX: '1',
PIP_DISABLE_PIP_VERSION_CHECK: '1',
},
});
const install = api.getInstallCommand(1, 'requirements.txt');
assert.doesNotMatch(install, /--prefix=/);
run(install);
assert.match(run(api.getGetCommand(1, 'ql-venv-fixture')), /1\.0/);
assert.equal(
run(
`'${path.join(
venv,
'bin/python3',
)}' -c 'import ql_venv_fixture; print(ql_venv_fixture.value)'`,
).trim(),
'42',
);
run(api.getUninstallCommand(1, 'requirements.txt'));
assert.throws(() =>
run(`'${path.join(venv, 'bin/python3')}' -c 'import ql_venv_fixture'`),
);
});
+69
View File
@@ -0,0 +1,69 @@
const test = require('node:test'),
assert = require('node:assert/strict'),
fs = require('node:fs'),
os = require('node:os'),
path = require('node:path'),
{ execFileSync } = require('node:child_process');
const load = require('../helpers/load-security-module.cjs');
test('venv installs, uninstalls and probes use the selected Python, including spaces', () => {
const methods = load('back/config/util.ts', {
'./index': { rootPath: '/application' },
'./const': {
PYTHON_VENV_DIR: 'virtual env',
PYTHON_INSTALL_DIR: '/legacy',
},
'../loaders/logger': {},
'../shared/utils': {},
'../data/dependence': {
DependenceTypes: { nodejs: 0, python3: 1, linux: 2 },
},
});
for (const name of [
'getInstallCommand',
'getUninstallCommand',
'getGetCommand',
]) {
const cmd = methods[name](1, 'requests');
assert.match(cmd, /'\/application\/virtual env\/bin\/python3'/);
assert.doesNotMatch(cmd, /--prefix=/);
}
});
test('venv setup keeps Python packages isolated and retains existing startup configuration', (t) => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'ql-venv-'));
t.after(() => fs.rmSync(dir, { recursive: true, force: true }));
const ql = path.join(dir, 'application with spaces');
fs.mkdirSync(ql);
const venv = path.join(dir, 'venv with spaces');
execFileSync('python3', ['-m', 'venv', venv]);
const source = fs.readFileSync('shell/start-simplify.sh', 'utf8');
const snippet = source.slice(
source.indexOf('export PNPM_HOME='),
source.indexOf('\ncd "${QL_DIR}"'),
);
const result = execFileSync(
'/bin/bash',
[
'-c',
snippet +
'\npython3 -c "import sys; print(sys.prefix); print(sys.base_prefix)"',
],
{
env: {
...process.env,
QL_DIR: ql,
QL_DATA_DIR: path.join(dir, 'data'),
PYTHON_VENV_DIR: venv,
PYTHONPATH: '/legacy/modules',
PYTHONUSERBASE: '/legacy',
PATH: process.env.PATH,
},
encoding: 'utf8',
},
)
.trim()
.split('\n');
assert.equal(fs.realpathSync(result[0]), fs.realpathSync(venv));
assert.notEqual(result[1], venv);
assert.match(source, /set -C; cat .env.example > .env/);
assert.doesNotMatch(source, /npm install|pip3 install|chmod 777/);
});
+2 -2
View File
@@ -7,8 +7,8 @@ const { spawnSync } = require('node:child_process');
// Run the actual startup prefix, stopping before service initialization.
const source = fs
.readFileSync('shell/start.sh', 'utf8')
.split('. ${QL_DIR}/shell/share.sh')[0];
.readFileSync('shell/start-simplify.sh', 'utf8')
.split('. "${QL_DIR}/shell/share.sh"')[0];
for (const command of ['start', 'reload']) {
for (const existing of [false, true]) {
test(`${command} ${