131 lines
4.8 KiB
Python
131 lines
4.8 KiB
Python
from __future__ import annotations
|
|
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
CHAT_JS = ROOT / "src" / "opensquilla" / "gateway" / "static" / "js" / "views" / "chat.js"
|
|
|
|
|
|
def test_web_chat_defaults_are_policy_driven() -> None:
|
|
source = CHAT_JS.read_text(encoding="utf-8")
|
|
|
|
assert "const _RUN_MODE_FALLBACK = 'trusted';" in source
|
|
assert "_applyHelloRunModePolicy" in source
|
|
assert "_runModePolicyDefault" in source
|
|
assert "new Set(['standard', 'trusted']);" in source
|
|
assert "Establish sandbox" in source
|
|
|
|
|
|
def _extract_js_function(source: str, name: str) -> str:
|
|
start = source.index(f"function {name}(")
|
|
brace = source.index("{", start)
|
|
depth = 0
|
|
for index in range(brace, len(source)):
|
|
char = source[index]
|
|
if char == "{":
|
|
depth += 1
|
|
elif char == "}":
|
|
depth -= 1
|
|
if depth == 0:
|
|
return source[start : index + 1]
|
|
raise AssertionError(f"could not extract JS function {name}")
|
|
|
|
|
|
def test_web_chat_run_mode_policy_fails_closed_for_missing_or_malformed_policy() -> None:
|
|
source = CHAT_JS.read_text(encoding="utf-8")
|
|
helper_names = [
|
|
"_runModePolicyValue",
|
|
"_normalizeRunModePolicyValue",
|
|
"_runModeAllowed",
|
|
"_firstAllowedRunMode",
|
|
"_clampRunMode",
|
|
"_readStoredRunModePreference",
|
|
"_clearStoredRunModePreference",
|
|
"_fullHostAccessDisabledMessage",
|
|
"_applyHelloRunModePolicy",
|
|
]
|
|
helpers = "\n".join(_extract_js_function(source, name) for name in helper_names)
|
|
script = f"""
|
|
const assert = require('assert');
|
|
const _RUN_MODE_FALLBACK = 'trusted';
|
|
const _RUN_MODE_PREF_KEY = 'opensquilla.chat.runMode';
|
|
let _runModePolicyDefault = _RUN_MODE_FALLBACK;
|
|
let _allowedRunModes = new Set(['standard', 'trusted', 'full']);
|
|
let _fullHostAccessDisabledReason = null;
|
|
let _runMode = _runModePolicyDefault;
|
|
let _runModeUserSelected = false;
|
|
let _toolbarState = {{ runMode: _runModePolicyDefault }};
|
|
const localStorage = {{ getItem() {{ return null; }}, removeItem() {{}}, setItem() {{}} }};
|
|
function _updateRunModeControl() {{}}
|
|
function _refreshToolbarTriggerGlow() {{}}
|
|
{helpers}
|
|
|
|
_applyHelloRunModePolicy(null);
|
|
assert.deepStrictEqual([..._allowedRunModes].sort(), ['standard', 'trusted']);
|
|
assert.strictEqual(_runModePolicyDefault, 'trusted');
|
|
assert.strictEqual(_runMode, 'trusted');
|
|
assert.strictEqual(_fullHostAccessDisabledReason, 'owner_required');
|
|
|
|
_applyHelloRunModePolicy({{ auth: {{ runModePolicy: {{
|
|
allowedRunModes: ['unknown'],
|
|
defaultRunMode: 'full',
|
|
}} }} }});
|
|
assert.deepStrictEqual([..._allowedRunModes].sort(), ['standard', 'trusted']);
|
|
assert.strictEqual(_runModePolicyDefault, 'trusted');
|
|
assert.strictEqual(_runMode, 'trusted');
|
|
assert.strictEqual(_fullHostAccessDisabledReason, 'owner_required');
|
|
|
|
_applyHelloRunModePolicy({{ auth: {{ runModePolicy: {{
|
|
allowedRunModes: ['standard', 'trusted', 'full'],
|
|
}} }} }});
|
|
assert.deepStrictEqual([..._allowedRunModes].sort(), ['full', 'standard', 'trusted']);
|
|
assert.strictEqual(_runModePolicyDefault, 'full');
|
|
assert.strictEqual(_runMode, 'full');
|
|
assert.strictEqual(_fullHostAccessDisabledReason, null);
|
|
|
|
_applyHelloRunModePolicy({{ auth: {{ runModePolicy: {{
|
|
allowedRunModes: ['standard', 'trusted'],
|
|
defaultRunMode: 'full',
|
|
fullHostAccessDisabledReason: 'owner_required',
|
|
}} }} }});
|
|
assert.deepStrictEqual([..._allowedRunModes].sort(), ['standard', 'trusted']);
|
|
assert.strictEqual(_runModePolicyDefault, 'trusted');
|
|
assert.strictEqual(_runMode, 'trusted');
|
|
assert.strictEqual(_fullHostAccessDisabledReason, 'owner_required');
|
|
"""
|
|
result = subprocess.run(
|
|
["node", "-e", script],
|
|
cwd=ROOT,
|
|
capture_output=True,
|
|
text=True,
|
|
check=False,
|
|
)
|
|
|
|
assert result.returncode == 0, result.stdout + result.stderr
|
|
|
|
|
|
def test_web_chat_run_mode_switch_uses_setup_gate_for_sandbox_modes() -> None:
|
|
source = CHAT_JS.read_text(encoding="utf-8")
|
|
|
|
assert "_requestSandboxSetupForMode" in source
|
|
assert "_ensureSandboxSetupOnly" in source
|
|
assert "_sandboxSetupReadyForMode" in source
|
|
assert "const pendingPrompt = !!_pendingSandboxSetupMode;" in source
|
|
assert "const optionalPrompt = setupKnown && !_sandboxSetupPromptDismissed;" in source
|
|
assert "sandbox.setup.status" in source
|
|
assert "sandbox.setup.ensure" in source
|
|
assert "if (mode === 'full') return true;" in source
|
|
assert "if (!(await _requestSandboxSetupForMode(mode))) return;" in source
|
|
|
|
|
|
def test_web_chat_run_mode_is_not_loaded_from_gateway_or_session_context() -> None:
|
|
source = CHAT_JS.read_text(encoding="utf-8")
|
|
|
|
assert "_loadRunModeStatusFallback" not in source
|
|
assert "sandbox.status" not in source
|
|
assert "_loadRunContext" not in source
|
|
assert "_syncRunMode" not in source
|
|
assert "sandbox.run_context.get" not in source
|
|
assert "sandbox.run_context.set" not in source
|