fix(settings): constrain plan path remapping (#1203)
Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
This commit is contained in:
committed by
GitHub
co-authored by
Isaac Sanchez
parent
3228e7f45f
commit
89630af98d
@@ -105,12 +105,13 @@ export const createSettingsRuntime = (deps) => {
|
||||
return entry;
|
||||
}
|
||||
const trimmedPath = entry.path.trim();
|
||||
if (!trimmedPath.startsWith(fromDir)) {
|
||||
const relativePath = path.relative(fromDir, trimmedPath);
|
||||
if (relativePath && (relativePath.startsWith('..') || path.isAbsolute(relativePath))) {
|
||||
return entry;
|
||||
}
|
||||
return {
|
||||
...entry,
|
||||
path: `${toDir}${trimmedPath.slice(fromDir.length)}`,
|
||||
path: relativePath ? path.join(toDir, relativePath) : toDir,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import crypto from 'crypto';
|
||||
import fsPromises from 'fs/promises';
|
||||
import os from 'os';
|
||||
import path from 'path';
|
||||
import { createProjectIdFromPath } from '../projects/project-id.js';
|
||||
import { createSettingsRuntime } from './settings-runtime.js';
|
||||
|
||||
const createRuntime = async () => {
|
||||
const tempRoot = await fsPromises.mkdtemp(path.join(os.tmpdir(), 'oc-settings-runtime-'));
|
||||
const settingsFilePath = path.join(tempRoot, 'settings.json');
|
||||
const runtime = createSettingsRuntime({
|
||||
fsPromises,
|
||||
path,
|
||||
crypto,
|
||||
SETTINGS_FILE_PATH: settingsFilePath,
|
||||
sanitizeProjects: (projects) => Array.isArray(projects) ? projects : [],
|
||||
sanitizeSettingsUpdate: (settings) => settings,
|
||||
mergePersistedSettings: (_current, changes) => changes,
|
||||
normalizeSettingsPaths: (settings) => ({ settings, changed: false }),
|
||||
normalizeStringArray: (values) => Array.isArray(values) ? values.filter((value) => typeof value === 'string') : [],
|
||||
formatSettingsResponse: (settings) => settings,
|
||||
resolveDirectoryCandidate: (value) => value,
|
||||
normalizeManagedRemoteTunnelHostname: (value) => value,
|
||||
normalizeManagedRemoteTunnelPresets: (value) => value,
|
||||
normalizeManagedRemoteTunnelPresetTokens: (value) => value,
|
||||
syncManagedRemoteTunnelConfigWithPresets: async () => {},
|
||||
upsertManagedRemoteTunnelToken: async () => {},
|
||||
});
|
||||
|
||||
return {
|
||||
runtime,
|
||||
settingsFilePath,
|
||||
tempRoot,
|
||||
cleanup: async () => {
|
||||
await fsPromises.rm(tempRoot, { recursive: true, force: true });
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
describe('settings runtime', () => {
|
||||
it('only remaps project plan paths within the migrated storage directory', async () => {
|
||||
const { runtime, settingsFilePath, tempRoot, cleanup } = await createRuntime();
|
||||
try {
|
||||
const projectPath = path.join(tempRoot, 'project');
|
||||
const oldProjectId = 'legacy-project-id';
|
||||
const newProjectId = createProjectIdFromPath(projectPath);
|
||||
const projectsRoot = path.join(path.dirname(settingsFilePath), 'projects');
|
||||
const oldStorageDir = path.join(projectsRoot, oldProjectId);
|
||||
const newStorageDir = path.join(projectsRoot, newProjectId);
|
||||
const siblingStorageDir = `${oldStorageDir}-sibling`;
|
||||
|
||||
await fsPromises.mkdir(projectPath, { recursive: true });
|
||||
await fsPromises.mkdir(projectsRoot, { recursive: true });
|
||||
await fsPromises.writeFile(
|
||||
settingsFilePath,
|
||||
JSON.stringify({
|
||||
projects: [{ id: oldProjectId, path: projectPath, addedAt: 1, lastOpenedAt: 1 }],
|
||||
activeProjectId: oldProjectId,
|
||||
}, null, 2),
|
||||
'utf8',
|
||||
);
|
||||
await fsPromises.writeFile(
|
||||
path.join(projectsRoot, `${oldProjectId}.json`),
|
||||
JSON.stringify({
|
||||
projectPlanFiles: [
|
||||
{ id: 'inside', path: path.join(oldStorageDir, 'plans', 'inside.md') },
|
||||
{ id: 'sibling', path: path.join(siblingStorageDir, 'plans', 'outside.md') },
|
||||
],
|
||||
}, null, 2),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
await runtime.readSettingsFromDiskMigrated();
|
||||
|
||||
const migratedConfig = JSON.parse(await fsPromises.readFile(path.join(projectsRoot, `${newProjectId}.json`), 'utf8'));
|
||||
expect(migratedConfig.projectPlanFiles).toEqual([
|
||||
{ id: 'inside', path: path.join(newStorageDir, 'plans', 'inside.md') },
|
||||
{ id: 'sibling', path: path.join(siblingStorageDir, 'plans', 'outside.md') },
|
||||
]);
|
||||
} finally {
|
||||
await cleanup();
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user