fix: parent-scoped file selection; never mutate the pySim model
pySim's lchan.select() resolves names against global selectables (self + parent chain + MF children + applications) and falls back to probe_file(), which blindly SELECTs an unknown FID and permanently injects a dynamically named DF.XXXX/EF.XXXX into the running filesystem model. Probing a whole tree or scanning a snapshot with custom files therefore polluted the model, made tree branches show children of the wrong object, and could persist phantom files into snapshots. - server: new _select_with_parent()/_select_path() walk the requested parent path (new parent_path field, parent_sel kept as legacy fallback) strictly through the model and call lchan.select_file() only; model-unknown 4-hex segments are probed only with allow_probe and the temporary child pySim adds is detached again via the cleanup callable that the four handlers (/api/tree|select|read|write) now run in a finally block - frontend: getParentPath() builds the segment chain (MF, ADF names, FIDs) and all tree/select/read/write bodies plus the snapshot/profile walker send parent_path; allow_probe is set only for custom files; the blind retries in the file manager were dropped - tests: tests/test_select_scope.py (duplicate-FID resolution, no APDU for unknown non-custom files, probe+detach, model unchanged); fs_load/fs_probe assertions for parent_path and allow_probe; docs/api.md and AGENTS.md document the contract; SW cache v117 -> v118.
This commit is contained in:
@@ -22,7 +22,7 @@ function extractFunc(src, name) {
|
||||
}
|
||||
|
||||
let code = 'var pysimFsTreeRoot = null;\nvar _pysimFsProbe = null;\nvar pysimFsSort = "fid";\n';
|
||||
for (const fn of ['getParentSel', 'pysimFsSortChildren', 'pysimFsLoadChildren', 'pysimFsSelectBody', 'pysimFsProbeUi', 'pysimFsProbeAll']) {
|
||||
for (const fn of ['getParentSel', 'getParentPath', 'pysimFsSortChildren', 'pysimFsLoadChildren', 'pysimFsSelectBody', 'pysimFsProbeUi', 'pysimFsProbeAll']) {
|
||||
code += extractFunc(html, fn) + '\n';
|
||||
}
|
||||
code += 'globalThis.esc = s => s;\nglobalThis.t = s => s;\nglobalThis.pysimCustomInject = () => {};\n';
|
||||
@@ -123,6 +123,19 @@ test('stop halts the walk and still reports a summary', async () => {
|
||||
assert.strictEqual(els['pysim-fs-probe-btn'].textContent, 'Probe all files');
|
||||
});
|
||||
|
||||
test('select bodies carry the parent path and set allow_probe only for custom files', async () => {
|
||||
root([df('DF.A', '5f01'), Object.assign(ef('EF.CUSTOM', '6fcc'), { custom: true })]);
|
||||
const body = pysimFsSelectBody(pysimFsTreeRoot.children[0]);
|
||||
assert.deepStrictEqual(body.parent_path, ['MF']);
|
||||
assert.strictEqual(body.parent_sel, 'MF');
|
||||
assert.ok(!body.allow_probe);
|
||||
const custom = pysimFsSelectBody(pysimFsTreeRoot.children[1]);
|
||||
assert.deepStrictEqual(custom.parent_path, ['MF']);
|
||||
assert.strictEqual(custom.allow_probe, true);
|
||||
const nested = { name: 'EF.1', fid: '6f01', isDir: false, parent: pysimFsTreeRoot.children[0] };
|
||||
assert.deepStrictEqual(pysimFsSelectBody(nested).parent_path, ['MF', '5f01']);
|
||||
});
|
||||
|
||||
test('children of an absent directory are neither fetched nor selected', async () => {
|
||||
const stale = Object.assign(df('DF.C', '5f03'), {
|
||||
exists: false,
|
||||
|
||||
Reference in New Issue
Block a user