fix(acl): separate scope enumeration from store-level bypass#226
Conversation
|
I think this PR has real value, and I’d support bringing it in once the current conflicts with What I like here is that this is not “abstraction for abstraction’s sake”. It fixes a genuine semantic mix-up that was spread across multiple layers:
The separation introduced here (
That is a worthwhile cleanup because it improves correctness, not just code style. I also like that this is backed by targeted regression coverage rather than just comments/docstrings. I ran the new ACL/scope tests locally, and I also ran the full The one practical caveat is that GitHub currently shows this PR as So my read is:
|
核心变更: - ACL 键规范化:trim agentAccess 键,避免空白填充导致 ACL 失效 - 明确 deny-all 语义:scopeFilter=[] 拒绝所有读写,与 undefined(绕过)区分 - 统一 hook agentId 解析:before_agent_start 和 before_prompt_build 均使用 resolveHookAgentId - importConfig 原子性:验证失败时警告不泄漏,配置完整回滚 - 保留 bypass ID 限制:system/undefined 仅限内部使用,拒绝配置和 sessionKey 解析 - reflection 加载安全:bypass 调用方使用可选 scopeFilter,避免过滤绕过 新增测试: - test/reflection-bypass-hook.test.mjs - hook bypass 和 sessionKey 解析 - test/scope-access-undefined.test.mjs - bypass ID 拒绝和 scope 访问 - test/smart-extractor-scope-filter.test.mjs - SmartExtractor scopeFilter 语义 - test/store-empty-scope-filter.test.mjs - 空数组 deny-all 语义 影响范围:6 个源文件 + 4 个测试文件,约 970 行净变动
422940d to
a1185c2
Compare
|
Rebased onto current master and resolved the remaining conflict. |
…on (CortexReach#226) 核心变更: - ACL 键规范化:trim agentAccess 键,避免空白填充导致 ACL 失效 - 明确 deny-all 语义:scopeFilter=[] 拒绝所有读写,与 undefined(绕过)区分 - 统一 hook agentId 解析:before_agent_start 和 before_prompt_build 均使用 resolveHookAgentId - importConfig 原子性:验证失败时警告不泄漏,配置完整回滚 - 保留 bypass ID 限制:system/undefined 仅限内部使用,拒绝配置和 sessionKey 解析 - reflection 加载安全:bypass 调用方使用可选 scopeFilter,避免过滤绕过 新增测试: - test/reflection-bypass-hook.test.mjs - hook bypass 和 sessionKey 解析 - test/scope-access-undefined.test.mjs - bypass ID 拒绝和 scope 访问 - test/smart-extractor-scope-filter.test.mjs - SmartExtractor scopeFilter 语义 - test/store-empty-scope-filter.test.mjs - 空数组 deny-all 语义 影响范围:6 个源文件 + 4 个测试文件,约 970 行净变动
…on (CortexReach#226) 核心变更: - ACL 键规范化:trim agentAccess 键,避免空白填充导致 ACL 失效 - 明确 deny-all 语义:scopeFilter=[] 拒绝所有读写,与 undefined(绕过)区分 - 统一 hook agentId 解析:before_agent_start 和 before_prompt_build 均使用 resolveHookAgentId - importConfig 原子性:验证失败时警告不泄漏,配置完整回滚 - 保留 bypass ID 限制:system/undefined 仅限内部使用,拒绝配置和 sessionKey 解析 - reflection 加载安全:bypass 调用方使用可选 scopeFilter,避免过滤绕过 新增测试: - test/reflection-bypass-hook.test.mjs - hook bypass 和 sessionKey 解析 - test/scope-access-undefined.test.mjs - bypass ID 拒绝和 scope 访问 - test/smart-extractor-scope-filter.test.mjs - SmartExtractor scopeFilter 语义 - test/store-empty-scope-filter.test.mjs - 空数组 deny-all 语义 影响范围:6 个源文件 + 4 个测试文件,约 970 行净变动
Summary
This PR separates scope enumeration from store-level bypass handling by introducing an explicit store-layer scope filter API (
getScopeFilter()/resolveScopeFilter(...)) instead of inferring bypass fromgetAccessibleScopes().It also fixes related consistency issues in hook agentId resolution,
importConfig()validation, ACL key normalization, reserved bypass ID handling, and reflection loading.What changed
Scope enumeration vs. bypass
getScopeFilter(agentId?)andresolveScopeFilter(...)as the explicit store-layer API for bypass signalinggetAccessibleScopes()as pure enumeration — it no longer implies store bypass[]denies all reads/writes, whileundefinedmeans bypasslist(),delete(), andupdate()pathsHook agentId resolution
before_agent_startandbefore_prompt_buildto both useresolveHookAgentId()withsessionKeyfallbackbefore_prompt_buildfalling back to hardcoded"main"when onlyctx.sessionKeyis availableImportConfig validation
importConfig()now suppressesconsole.warnduring validation; warnings emit only after successful validationACL hardening
agentAccesskeys (trim on construction/import) to prevent whitespace-related failuressystem,undefined) in both config and session key parsingReflection loading
scopeFilteris omittedreflection:agent:{id}even with explicitagentAccessconfigCompatibility notes
Custom or legacy scope managers must implement
getScopeFilter()explicitly if they need store-level bypass behavior.Returning
[]fromgetAccessibleScopes()should no longer be relied on to imply bypass. The legacy fallback remains supported for compatibility, but bypass is now defined as an explicit store-layer behavior.Tests
Added / expanded regression coverage for:
test/reflection-bypass-hook.test.mjs— hook bypass and sessionKey agentId resolutiontest/scope-access-undefined.test.mjs— bypass ID rejection and scope access controltest/smart-extractor-scope-filter.test.mjs— SmartExtractor scopeFilter semanticstest/store-empty-scope-filter.test.mjs— empty array deny-all semanticsAll existing tests continue to pass.