diff --git a/bin/lib/policies.js b/bin/lib/policies.js index c997ca0a46..3f9cdcab8b 100644 --- a/bin/lib/policies.js +++ b/bin/lib/policies.js @@ -69,6 +69,20 @@ function parseCurrentPolicy(raw) { return raw.slice(sep + 3).trim(); } +/** + * Build the openshell policy set command with properly quoted arguments. + */ +function buildPolicySetCommand(policyFile, sandboxName) { + return `openshell policy set --policy "${policyFile}" --wait "${sandboxName}"`; +} + +/** + * Build the openshell policy get command with properly quoted arguments. + */ +function buildPolicyGetCommand(sandboxName) { + return `openshell policy get --full "${sandboxName}" 2>/dev/null`; +} + function applyPreset(sandboxName, presetName) { const presetContent = loadPreset(presetName); if (!presetContent) { @@ -86,7 +100,7 @@ function applyPreset(sandboxName, presetName) { let rawPolicy = ""; try { rawPolicy = runCapture( - `openshell policy get --full "${sandboxName}" 2>/dev/null`, + buildPolicyGetCommand(sandboxName), { ignoreError: true } ); } catch {} @@ -146,7 +160,8 @@ function applyPreset(sandboxName, presetName) { fs.writeFileSync(tmpFile, merged, "utf-8"); try { - run(`openshell policy set --policy "${tmpFile}" --wait "${sandboxName}"`); + run(buildPolicySetCommand(tmpFile, sandboxName)); + console.log(` Applied preset: ${presetName}`); } finally { fs.unlinkSync(tmpFile); @@ -175,6 +190,10 @@ module.exports = { listPresets, loadPreset, getPresetEndpoints, + extractPresetEntries, + parseCurrentPolicy, + buildPolicySetCommand, + buildPolicyGetCommand, applyPreset, getAppliedPresets, }; diff --git a/test/policies.test.js b/test/policies.test.js index 3d8f13c380..ec1a021217 100644 --- a/test/policies.test.js +++ b/test/policies.test.js @@ -65,6 +65,32 @@ describe("policies", () => { }); }); + describe("buildPolicySetCommand", () => { + it("quotes sandbox name to prevent argument splitting", () => { + const cmd = policies.buildPolicySetCommand("/tmp/policy.yaml", "my-assistant"); + assert.equal(cmd, 'openshell policy set --policy "/tmp/policy.yaml" --wait "my-assistant"'); + }); + + it("handles sandbox names with spaces", () => { + const cmd = policies.buildPolicySetCommand("/tmp/policy.yaml", "my sandbox"); + assert.ok(cmd.includes('"my sandbox"'), "sandbox name must be quoted"); + }); + + it("places --wait before the sandbox name", () => { + const cmd = policies.buildPolicySetCommand("/tmp/policy.yaml", "test-box"); + const waitIdx = cmd.indexOf("--wait"); + const nameIdx = cmd.indexOf('"test-box"'); + assert.ok(waitIdx < nameIdx, "--wait must come before sandbox name"); + }); + }); + + describe("buildPolicyGetCommand", () => { + it("quotes sandbox name", () => { + const cmd = policies.buildPolicyGetCommand("my-assistant"); + assert.equal(cmd, 'openshell policy get --full "my-assistant" 2>/dev/null'); + }); + }); + describe("preset YAML schema", () => { it("no preset has rules at NetworkPolicyRuleDef level", () => { // rules must be inside endpoints, not as sibling of endpoints/binaries