6.7 KiB
Policy System Bug Fix Summary
Bug Report
Issue: Kind 1 events were being accepted even though the policy whitelist only contained kind 4678.
Root Cause Analysis
The relay had TWO critical bugs in the policy system that worked together to create a security vulnerability:
Bug #1: Hardcoded return true in checkKindsPolicy()
Location: pkg/policy/policy.go:1010
// BEFORE (BUG):
// No specific rules (maybe global rule exists) - allow all kinds
return true
// AFTER (FIXED):
// No specific rules (maybe global rule exists) - fall back to default policy
return p.getDefaultPolicyAction()
Problem: When no whitelist, blacklist, or rules were present, the function returned true unconditionally, ignoring the default_policy configuration.
Impact: Empty policy configurations would allow ALL event kinds.
Bug #2: Silent Failure on Config Load Error
Location: pkg/policy/policy.go:363-378
// BEFORE (BUG):
if err := policy.LoadFromFile(configPath); err != nil {
log.W.F("failed to load policy configuration from %s: %v", configPath, err)
log.I.F("using default policy configuration")
}
// AFTER (FIXED):
if err := policy.LoadFromFile(configPath); err != nil {
log.E.F("FATAL: Policy system is ENABLED (ORLY_POLICY_ENABLED=true) but configuration failed to load from %s: %v", configPath, err)
log.E.F("The relay cannot start with an invalid policy configuration.")
log.E.F("Fix: Either disable the policy system (ORLY_POLICY_ENABLED=false) or ensure %s exists and contains valid JSON", configPath)
panic(fmt.Sprintf("fatal policy configuration error: %v", err))
}
Problem: When policy was enabled but policy.json failed to load:
- Only logged a WARNING (not fatal)
- Continued with empty policy object (no whitelist, no rules)
- Empty policy + Bug #1 = allowed ALL events
- Relay appeared to be "protected" but was actually wide open
Impact: Critical security vulnerability - misconfigured policy files would silently allow all events.
Combined Effect
When a relay operator:
- Enabled policy system (
ORLY_POLICY_ENABLED=true) - Had a missing, malformed, or inaccessible
policy.jsonfile
The relay would:
- ❌ Log "policy allowed event" (appearing to work)
- ❌ Have empty whitelist/rules (silent failure)
- ❌ Fall through to hardcoded
return true(Bug #1) - ✅ Allow ALL event kinds (complete bypass)
Fixes Applied
Fix #1: Respect default_policy Setting
Changed checkKindsPolicy() to return p.getDefaultPolicyAction() instead of hardcoded true.
Result: When no whitelist/rules exist, the policy respects the default_policy configuration (either "allow" or "deny").
Fix #2: Fail-Fast on Config Error
Changed NewWithManager() to panic immediately if policy is enabled but config fails to load.
Result: Relay refuses to start with invalid configuration, forcing operator to fix it.
Test Coverage
New Tests Added
TestBugFix_FailSafeWhenConfigMissing- Verifies panic on missing configTestBugFix_EmptyWhitelistRespectsDefaultPolicy- Tests both deny and allow defaultsTestBugReproduction_*- Reproduces the exact scenario from the bug report
Existing Tests Updated
TestNewWithManager- Now handles both enabled and disabled policy scenarios- All existing whitelist tests continue to pass ✅
Behavior Changes
Before Fix
Policy System: ENABLED ✅
Config File: MISSING ❌
Logs: "failed to load policy configuration" (warning)
Result: Allow ALL events 🚨
Policy System: ENABLED ✅
Config File: { "whitelist": [4678] } ✅
Logs: "policy allowed event" for kind 1
Result: Allow kind 1 event 🚨
After Fix
Policy System: ENABLED ✅
Config File: MISSING ❌
Result: PANIC - relay refuses to start 🛑
Policy System: ENABLED ✅
Config File: { "whitelist": [4678] } ✅
Logs: "policy rejected event" for kind 1
Result: Reject kind 1 event ✅
Migration Guide for Operators
If Your Relay Panics After Upgrade
Error Message:
FATAL: Policy system is ENABLED (ORLY_POLICY_ENABLED=true) but configuration failed to load
panic: fatal policy configuration error: policy configuration file does not exist
Resolution Options:
-
Create valid
policy.json:mkdir -p ~/.config/ORLY cat > ~/.config/ORLY/policy.json << 'EOF' { "default_policy": "allow", "kind": { "whitelist": [1, 3, 4, 5, 6, 7] }, "rules": {} } EOF -
Disable policy system (temporary):
# In your systemd service file: Environment="ORLY_POLICY_ENABLED=false" sudo systemctl daemon-reload sudo systemctl restart orly
Security Impact
Severity: 🔴 CRITICAL
CVE-Like Description:
When
ORLY_POLICY_ENABLED=trueis set but the policy configuration file fails to load (missing file, permission error, or malformed JSON), the relay silently bypasses all policy checks and allows events of any kind, defeating the intended access control mechanism.
Affected Versions: All versions prior to this fix
Fixed Versions: Current HEAD after commit [TBD]
CVSS-like: Configuration-dependent vulnerability requiring operator misconfiguration
Verification
To verify the fix is working:
-
Test with valid config:
# Should start normally ORLY_POLICY_ENABLED=true ./orly # Logs: "loaded policy configuration from ~/.config/ORLY/policy.json" -
Test with missing config:
# Should panic immediately mv ~/.config/ORLY/policy.json ~/.config/ORLY/policy.json.bak ORLY_POLICY_ENABLED=true ./orly # Expected: FATAL error and panic -
Test whitelist enforcement:
# Create whitelist with only kind 4678 echo '{"kind":{"whitelist":[4678]},"rules":{}}' > ~/.config/ORLY/policy.json # Try to send kind 1 event # Expected: "policy rejected event" or "event blocked by policy"
Files Modified
pkg/policy/policy.go- Core fixespkg/policy/bug_reproduction_test.go- New test filepkg/policy/policy_test.go- Updated existing tests
Related Documentation
- Policy Usage Guide
- Policy Troubleshooting
- CLAUDE.md - Build and configuration instructions
Credits
Bug Reported By: User via client relay (relay1.zenotp.app)
Root Cause Analysis: Deep investigation of policy evaluation flow
Fix Verified: All tests passing, including reproduction of original bug scenario