diff --git a/.claude/settings.local.json b/.claude/settings.local.json
index afde6e1..2c6ad60 100644
--- a/.claude/settings.local.json
+++ b/.claude/settings.local.json
@@ -47,7 +47,8 @@
"Bash(git add:*)",
"Bash(./test-policy.sh:*)",
"Bash(docker rm:*)",
- "Bash(./scripts/docker-policy/test-policy.sh:*)"
+ "Bash(./scripts/docker-policy/test-policy.sh:*)",
+ "Bash(./policytest:*)"
],
"deny": [],
"ask": []
diff --git a/app/web/dist/index.html b/app/web/dist/index.html
index 9daeafb..e231052 100644
--- a/app/web/dist/index.html
+++ b/app/web/dist/index.html
@@ -1 +1,17 @@
-test
+
+
+
+
+
+
+ ORLY?
+
+
+
+
+
+
+
+
+
+
diff --git a/cmd/policytest/main.go b/cmd/policytest/main.go
index 83caa66..305f441 100644
--- a/cmd/policytest/main.go
+++ b/cmd/policytest/main.go
@@ -8,20 +8,23 @@ import (
"lol.mleku.dev/chk"
"lol.mleku.dev/log"
- "next.orly.dev/pkg/interfaces/signer/p8k"
"next.orly.dev/pkg/encoders/event"
+ "next.orly.dev/pkg/encoders/filter"
"next.orly.dev/pkg/encoders/kind"
"next.orly.dev/pkg/encoders/tag"
+ "next.orly.dev/pkg/interfaces/signer/p8k"
"next.orly.dev/pkg/protocol/ws"
)
func main() {
var err error
url := flag.String("url", "ws://127.0.0.1:3334", "relay websocket URL")
- timeout := flag.Duration("timeout", 20*time.Second, "publish timeout")
+ timeout := flag.Duration("timeout", 20*time.Second, "operation timeout")
+ testType := flag.String("type", "event", "test type: 'event' for write control, 'req' for read control, 'both' for both")
+ eventKind := flag.Int("kind", 4678, "event kind to test")
flag.Parse()
- // Minimal client that publishes a single kind 4678 event and reports OK/err
+ // Connect to relay
var rl *ws.Client
if rl, err = ws.RelayConnect(context.Background(), *url); chk.E(err) {
log.E.F("connect error: %v", err)
@@ -29,6 +32,7 @@ func main() {
}
defer rl.Close()
+ // Create signer
var signer *p8k.Signer
if signer, err = p8k.New(); chk.E(err) {
log.E.F("signer create error: %v", err)
@@ -39,26 +43,93 @@ func main() {
return
}
+ // Perform tests based on type
+ switch *testType {
+ case "event":
+ testEventWrite(rl, signer, *eventKind, *timeout)
+ case "req":
+ testReqRead(rl, signer, *eventKind, *timeout)
+ case "both":
+ log.I.Ln("Testing EVENT (write control)...")
+ testEventWrite(rl, signer, *eventKind, *timeout)
+ log.I.Ln("\nTesting REQ (read control)...")
+ testReqRead(rl, signer, *eventKind, *timeout)
+ default:
+ log.E.F("invalid test type: %s (must be 'event', 'req', or 'both')", *testType)
+ }
+}
+
+func testEventWrite(rl *ws.Client, signer *p8k.Signer, eventKind int, timeout time.Duration) {
ev := &event.E{
CreatedAt: time.Now().Unix(),
- Kind: kind.K{K: 4678}.K, // arbitrary custom kind
+ Kind: uint16(eventKind),
Tags: tag.NewS(),
- Content: []byte("policy test: expect rejection"),
+ Content: []byte("policy test: expect rejection for write"),
}
- if err = ev.Sign(signer); chk.E(err) {
+ if err := ev.Sign(signer); chk.E(err) {
log.E.F("sign error: %v", err)
return
}
- ctx, cancel := context.WithTimeout(context.Background(), *timeout)
+ ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
- if err = rl.Publish(ctx, ev); err != nil {
+ if err := rl.Publish(ctx, ev); err != nil {
// Expected path if policy rejects: client returns error with reason (from OK false)
- fmt.Println("policy reject:", err)
+ fmt.Println("EVENT policy reject:", err)
return
}
- log.I.Ln("publish result: accepted")
- fmt.Println("ACCEPT")
+ log.I.Ln("EVENT publish result: accepted")
+ fmt.Println("EVENT ACCEPT")
+}
+
+func testReqRead(rl *ws.Client, signer *p8k.Signer, eventKind int, timeout time.Duration) {
+ // First, publish a test event to the relay that we'll try to query
+ testEvent := &event.E{
+ CreatedAt: time.Now().Unix(),
+ Kind: uint16(eventKind),
+ Tags: tag.NewS(),
+ Content: []byte("policy test: event for read control test"),
+ }
+ if err := testEvent.Sign(signer); chk.E(err) {
+ log.E.F("sign error: %v", err)
+ return
+ }
+
+ ctx, cancel := context.WithTimeout(context.Background(), timeout)
+ defer cancel()
+
+ // Try to publish the test event first (ignore errors if policy rejects)
+ _ = rl.Publish(ctx, testEvent)
+ log.I.F("published test event kind %d for read testing", eventKind)
+
+ // Now try to query for events of this kind
+ limit := uint(10)
+ f := &filter.F{
+ Kinds: kind.FromIntSlice([]int{eventKind}),
+ Limit: &limit,
+ }
+
+ ctx2, cancel2 := context.WithTimeout(context.Background(), timeout)
+ defer cancel2()
+
+ events, err := rl.QuerySync(ctx2, f)
+ if chk.E(err) {
+ log.E.F("query error: %v", err)
+ fmt.Println("REQ query error:", err)
+ return
+ }
+
+ // Check if we got the expected events
+ if len(events) == 0 {
+ // Could mean policy filtered it out, or it wasn't stored
+ fmt.Println("REQ policy reject: no events returned (filtered by read policy)")
+ log.I.F("REQ result: no events of kind %d returned (policy filtered or not stored)", eventKind)
+ return
+ }
+
+ // Events were returned - read access allowed
+ fmt.Printf("REQ ACCEPT: %d events returned\n", len(events))
+ log.I.F("REQ result: %d events of kind %d returned", len(events), eventKind)
}
diff --git a/docs/POLICY_USAGE_GUIDE.md b/docs/POLICY_USAGE_GUIDE.md
index c98ff58..372d363 100644
--- a/docs/POLICY_USAGE_GUIDE.md
+++ b/docs/POLICY_USAGE_GUIDE.md
@@ -361,6 +361,255 @@ Place scripts in a secure location and reference them in policy:
Ensure scripts are executable and have appropriate permissions.
+### Script Requirements and Best Practices
+
+#### Critical Requirements
+
+**1. Output Only JSON to stdout**
+
+Scripts MUST write ONLY JSON responses to stdout. Any other output (debug messages, logs, etc.) will break the JSONL protocol and cause errors.
+
+```javascript
+// ❌ WRONG - This will cause "broken pipe" errors
+console.log("Policy script starting..."); // This goes to stdout!
+console.log(JSON.stringify(response)); // Correct
+
+// ✅ CORRECT - Use stderr or file for debug output
+console.error("Policy script starting..."); // This goes to stderr (OK)
+fs.appendFileSync('/tmp/policy.log', 'Starting...\n'); // This goes to file (OK)
+console.log(JSON.stringify(response)); // Stdout for JSON only
+```
+
+**2. Flush stdout After Each Response**
+
+Always flush stdout after writing a response to ensure immediate delivery:
+
+```python
+# Python
+print(json.dumps(response))
+sys.stdout.flush() # Critical!
+```
+
+```javascript
+// Node.js (usually automatic, but can be forced)
+process.stdout.write(JSON.stringify(response) + '\n');
+```
+
+**3. Run as a Long-Lived Process**
+
+Scripts should run continuously, reading from stdin in a loop. They should NOT:
+- Exit after processing one event
+- Use batch processing
+- Close stdin/stdout prematurely
+
+```javascript
+// ✅ CORRECT - Long-lived process
+const readline = require('readline');
+const rl = readline.createInterface({
+ input: process.stdin,
+ output: process.stdout,
+ terminal: false
+});
+
+rl.on('line', (line) => {
+ const event = JSON.parse(line);
+ const response = processEvent(event);
+ console.log(JSON.stringify(response));
+});
+```
+
+**4. Handle Errors Gracefully**
+
+Always catch errors and return a valid JSON response:
+
+```javascript
+rl.on('line', (line) => {
+ try {
+ const event = JSON.parse(line);
+ const response = processEvent(event);
+ console.log(JSON.stringify(response));
+ } catch (err) {
+ // Log to stderr or file, not stdout!
+ console.error(`Error: ${err.message}`);
+
+ // Return reject response
+ console.log(JSON.stringify({
+ id: '',
+ action: 'reject',
+ msg: 'Policy script error'
+ }));
+ }
+});
+```
+
+**5. Response Format**
+
+Every response MUST include these fields:
+
+```json
+{
+ "id": "event_id", // Must match input event ID
+ "action": "accept", // Must be: accept, reject, or shadowReject
+ "msg": "" // Required (can be empty string)
+}
+```
+
+#### Common Issues and Solutions
+
+**Broken Pipe Error**
+
+```
+ERROR: policy script /path/to/script.js stdin closed (broken pipe)
+```
+
+**Causes:**
+- Script exited prematurely
+- Script wrote non-JSON output to stdout
+- Script crashed or encountered an error
+- Script closed stdin/stdout incorrectly
+
+**Solutions:**
+1. Remove ALL `console.log()` statements except JSON responses
+2. Use `console.error()` or log files for debugging
+3. Add error handling to catch and log exceptions
+4. Ensure script runs continuously (doesn't exit)
+
+**Response Timeout**
+
+```
+WARN: policy script /path/to/script.js response timeout - script may not be responding correctly
+```
+
+**Causes:**
+- Script not flushing stdout
+- Script processing taking > 5 seconds
+- Script not responding to input
+- Non-JSON output consuming a response slot
+
+**Solutions:**
+1. Add `sys.stdout.flush()` (Python) after each response
+2. Optimize processing logic to be faster
+3. Check that script is reading from stdin correctly
+4. Remove debug output from stdout
+
+**Invalid JSON Response**
+
+```
+ERROR: failed to parse policy response from /path/to/script.js
+WARN: policy script produced non-JSON output on stdout: "Debug message"
+```
+
+**Solutions:**
+1. Validate JSON before outputting
+2. Use a JSON library, don't build strings manually
+3. Move debug output to stderr or files
+
+#### Testing Your Script
+
+Before deploying, test your script:
+
+```bash
+# 1. Test basic functionality
+echo '{"id":"test123","pubkey":"abc","kind":1,"content":"test","tags":[],"created_at":1234567890,"sig":"def"}' | node policy-script.js
+
+# 2. Check for non-JSON output
+echo '{"id":"test123","pubkey":"abc","kind":1,"content":"test","tags":[],"created_at":1234567890,"sig":"def"}' | node policy-script.js 2>/dev/null | jq .
+
+# 3. Test error handling
+echo 'invalid json' | node policy-script.js
+```
+
+Expected output (valid JSON only):
+```json
+{"id":"test123","action":"accept","msg":""}
+```
+
+#### Node.js Example (Complete)
+
+```javascript
+#!/usr/bin/env node
+
+const fs = require('fs');
+const readline = require('readline');
+
+// Use stderr or file for debug logging
+const logFile = '/tmp/policy-debug.log';
+function debug(msg) {
+ fs.appendFileSync(logFile, `${Date.now()}: ${msg}\n`);
+}
+
+// Create readline interface
+const rl = readline.createInterface({
+ input: process.stdin,
+ output: process.stdout,
+ terminal: false
+});
+
+debug('Policy script started');
+
+// Process each event
+rl.on('line', (line) => {
+ try {
+ const event = JSON.parse(line);
+ debug(`Processing event ${event.id}`);
+
+ // Your policy logic here
+ const action = shouldAccept(event) ? 'accept' : 'reject';
+
+ // ONLY JSON to stdout
+ console.log(JSON.stringify({
+ id: event.id,
+ action: action,
+ msg: action === 'reject' ? 'Policy rejected' : ''
+ }));
+
+ } catch (err) {
+ debug(`Error: ${err.message}`);
+
+ // Still return valid JSON
+ console.log(JSON.stringify({
+ id: '',
+ action: 'reject',
+ msg: 'Policy script error'
+ }));
+ }
+});
+
+rl.on('close', () => {
+ debug('Policy script stopped');
+});
+
+function shouldAccept(event) {
+ // Your policy logic
+ return !event.content.toLowerCase().includes('spam');
+}
+```
+
+#### Event Fields
+
+Scripts receive additional context fields:
+
+```json
+{
+ "id": "event_id",
+ "pubkey": "author_pubkey",
+ "kind": 1,
+ "content": "Event content",
+ "tags": [],
+ "created_at": 1234567890,
+ "sig": "signature",
+ "logged_in_pubkey": "authenticated_user_pubkey",
+ "ip_address": "127.0.0.1",
+ "access_type": "read"
+}
+```
+
+**access_type values:**
+- `"write"`: Event is being stored (EVENT message)
+- `"read"`: Event is being retrieved (REQ message)
+
+Use this to implement different policies for reads vs writes.
+
## Policy Evaluation Order
Events are evaluated in this order:
diff --git a/pkg/policy/policy.go b/pkg/policy/policy.go
index 5286e6e..5bb2ade 100644
--- a/pkg/policy/policy.go
+++ b/pkg/policy/policy.go
@@ -10,6 +10,7 @@ import (
"os"
"os/exec"
"path/filepath"
+ "strings"
"sync"
"time"
@@ -77,6 +78,7 @@ type PolicyEvent struct {
*event.E
LoggedInPubkey string `json:"logged_in_pubkey,omitempty"`
IPAddress string `json:"ip_address,omitempty"`
+ AccessType string `json:"access_type,omitempty"` // "read" or "write"
}
// MarshalJSON implements custom JSON marshaling for PolicyEvent.
@@ -109,6 +111,9 @@ func (pe *PolicyEvent) MarshalJSON() ([]byte, error) {
if pe.IPAddress != "" {
safeEvent["ip_address"] = pe.IPAddress
}
+ if pe.AccessType != "" {
+ safeEvent["access_type"] = pe.AccessType
+ }
return json.Marshal(safeEvent)
}
@@ -532,6 +537,17 @@ func (sr *ScriptRunner) ProcessEvent(evt *PolicyEvent) (
// Send the event JSON to the script (newline-terminated)
if _, err := stdin.Write(append(eventJSON, '\n')); chk.E(err) {
+ // Check if it's a broken pipe error, which means the script has died
+ if strings.Contains(err.Error(), "broken pipe") || strings.Contains(err.Error(), "closed pipe") {
+ log.E.F(
+ "policy script %s stdin closed (broken pipe) - script may have crashed or exited prematurely",
+ sr.scriptPath,
+ )
+ // Mark as not running so it will be restarted on next periodic check
+ sr.mutex.Lock()
+ sr.isRunning = false
+ sr.mutex.Unlock()
+ }
return nil, fmt.Errorf("failed to write event to script: %v", err)
}
@@ -541,6 +557,10 @@ func (sr *ScriptRunner) ProcessEvent(evt *PolicyEvent) (
log.D.S("response", response)
return &response, nil
case <-time.After(5 * time.Second):
+ log.W.F(
+ "policy script %s response timeout - script may not be responding correctly (check for debug output on stdout)",
+ sr.scriptPath,
+ )
return nil, fmt.Errorf("script response timeout")
case <-sr.ctx.Done():
return nil, fmt.Errorf("script context cancelled")
@@ -554,6 +574,7 @@ func (sr *ScriptRunner) readResponses() {
}
scanner := bufio.NewScanner(sr.stdout)
+ nonJSONLineCount := 0
for scanner.Scan() {
line := scanner.Text()
if line == "" {
@@ -562,10 +583,31 @@ func (sr *ScriptRunner) readResponses() {
log.D.F("policy response: %s", line)
var response PolicyResponse
if err := json.Unmarshal([]byte(line), &response); chk.E(err) {
- log.E.F(
- "failed to parse policy response from %s: %v", sr.scriptPath,
- err,
- )
+ // Check if this looks like debug output
+ if strings.HasPrefix(line, "{") {
+ // Looks like JSON but failed to parse
+ log.E.F(
+ "failed to parse policy response from %s: %v\nLine: %s",
+ sr.scriptPath, err, line,
+ )
+ } else {
+ // Definitely not JSON - probably debug output
+ nonJSONLineCount++
+ if nonJSONLineCount <= 3 {
+ log.W.F(
+ "policy script %s produced non-JSON output on stdout (should only output JSONL): %q",
+ sr.scriptPath, line,
+ )
+ } else if nonJSONLineCount == 4 {
+ log.W.F(
+ "policy script %s continues to produce non-JSON output - suppressing further warnings",
+ sr.scriptPath,
+ )
+ }
+ log.W.F(
+ "IMPORTANT: Policy scripts must ONLY write JSON responses to stdout. Use stderr or a log file for debug output.",
+ )
+ }
continue
}
@@ -984,6 +1026,7 @@ func (p *P) checkScriptPolicy(
E: ev,
LoggedInPubkey: hex.Enc(loggedInPubkey),
IPAddress: ipAddress,
+ AccessType: access,
}
// Process event through policy script
diff --git a/pkg/version/version b/pkg/version/version
index 58571f7..2e9c921 100644
--- a/pkg/version/version
+++ b/pkg/version/version
@@ -1 +1 @@
-v0.27.5
\ No newline at end of file
+v0.27.6
\ No newline at end of file
diff --git a/scripts/docker-policy/README.md b/scripts/docker-policy/README.md
index 1054cab..3b31d9f 100644
--- a/scripts/docker-policy/README.md
+++ b/scripts/docker-policy/README.md
@@ -21,9 +21,10 @@ test-docker-policy/
1. **Builds** an Ubuntu 22.04.5 Docker image with ORLY relay
2. **Configures** the policy engine with `cs-policy.js`
3. **Starts** the relay with policy engine enabled
-4. **Sends** a test event to the relay
-5. **Verifies** that `cs-policy.js` created `/home/orly/cs-policy-output.txt`
-6. **Reports** success or failure
+4. **Tests EVENT messages** (write control) using the `policytest` tool
+5. **Tests REQ messages** (read control) using the `policytest` tool
+6. **Verifies** that `cs-policy.js` created `/home/orly/cs-policy-output.txt`
+7. **Reports** success or failure
## How cs-policy.js Works
@@ -46,9 +47,31 @@ if (fs.existsSync(filePath)) {
Run the automated test:
```bash
-./test-docker-policy/test-policy.sh
+./scripts/docker-policy/test-policy.sh
```
+## Policy Test Tool
+
+The `policytest` tool is a command-line utility for testing policy enforcement:
+
+```bash
+# Test write control (EVENT messages)
+./policytest -url ws://localhost:8777 -type event -kind 1
+
+# Test read control (REQ messages)
+./policytest -url ws://localhost:8777 -type req -kind 1
+
+# Test both write and read control
+./policytest -url ws://localhost:8777 -type both -kind 1
+```
+
+### Options
+
+- `-url` - Relay WebSocket URL (default: `ws://127.0.0.1:3334`)
+- `-type` - Test type: `event` for write control, `req` for read control, `both` for both (default: `event`)
+- `-kind` - Event kind to test (default: `4678`)
+- `-timeout` - Operation timeout (default: `20s`)
+
## Manual Testing
### 1. Build and Start Container
diff --git a/scripts/docker-policy/cs-policy-daemon.js b/scripts/docker-policy/cs-policy-daemon.js
index d4218ff..5810ece 100644
--- a/scripts/docker-policy/cs-policy-daemon.js
+++ b/scripts/docker-policy/cs-policy-daemon.js
@@ -24,8 +24,10 @@ rl.on('line', (line) => {
// Parse the policy event
const event = JSON.parse(line);
- // Log event details
- fs.appendFileSync(filePath, `${Date.now()}: Event ID: ${event.id || 'unknown'}\n`);
+ // Log event details including access type
+ const accessType = event.access_type || 'unknown';
+ const eventKind = event.kind || 'unknown';
+ fs.appendFileSync(filePath, `${Date.now()}: Event ID: ${event.id || 'unknown'}, Kind: ${eventKind}, Access: ${accessType}\n`);
// Respond with "accept" to allow the event
const response = {
diff --git a/scripts/docker-policy/environment.txt b/scripts/docker-policy/environment.txt
index 68eca3f..ffb23ae 100644
--- a/scripts/docker-policy/environment.txt
+++ b/scripts/docker-policy/environment.txt
@@ -3,5 +3,5 @@ ORLY_APP_NAME="orly"
ORLY_PUBLIC_READABLE=true
ORLY_PRIVATE=false
ORLY_OWNERS=4db2c42f3c02079dd6feae3f88f6c8693940a00ade3cc8e5d72050bd6e577cd5
-ORLY_LOG_LEVEL=debug
+ORLY_LOG_LEVEL=trace
ORLY_POLICY_ENABLED=true
diff --git a/scripts/docker-policy/test-policy.sh b/scripts/docker-policy/test-policy.sh
index 9d658d2..eeca83f 100755
--- a/scripts/docker-policy/test-policy.sh
+++ b/scripts/docker-policy/test-policy.sh
@@ -49,17 +49,11 @@ echo -e "${YELLOW}Step 7: Checking relay logs...${NC}"
docker logs orly-policy-test 2>&1 | tail -20
echo ""
-echo -e "${YELLOW}Step 8: Sending test event to relay...${NC}"
+echo -e "${YELLOW}Step 8: Building policytest tool...${NC}"
+cd "$REPO_ROOT" && CGO_ENABLED=0 go build -o policytest ./cmd/policytest
-# Install websocat if not available
-if ! command -v websocat &> /dev/null; then
- echo "websocat not found. Installing..."
- wget -qO- https://github.com/vi/websocat/releases/download/v1.12.0/websocat.x86_64-unknown-linux-musl -O /tmp/websocat
- chmod +x /tmp/websocat
- WEBSOCAT="/tmp/websocat"
-else
- WEBSOCAT="websocat"
-fi
+echo ""
+echo -e "${YELLOW}Step 9: Testing EVENT message (write control)...${NC}"
# Check which port the relay is listening on
RELAY_PORT=$(docker logs orly-policy-test 2>&1 | grep "starting listener" | grep -oP ':\K[0-9]+' | head -1)
@@ -68,20 +62,30 @@ if [ -z "$RELAY_PORT" ]; then
fi
echo "Relay is listening on port: $RELAY_PORT"
-# Generate a test event with a properly formatted (but invalid) signature
-# The policy script should still receive this event even if validation fails
-TIMESTAMP=$(date +%s)
-TEST_EVENT='["EVENT",{"id":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","pubkey":"4db2c42f3c02079dd6feae3f88f6c8693940a00ade3cc8e5d72050bd6e577cd5","created_at":'$TIMESTAMP',"kind":1,"tags":[],"content":"Test event for policy validation","sig":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}]'
-
-echo "Sending test event..."
-echo "$TEST_EVENT" | timeout 5 $WEBSOCAT ws://localhost:$RELAY_PORT 2>&1 || echo "Connection attempt completed"
+# Test EVENT message
+cd "$REPO_ROOT"
+./policytest -url "ws://localhost:$RELAY_PORT" -type event -kind 1 2>&1 || echo "EVENT test completed"
echo ""
-echo -e "${YELLOW}Step 9: Waiting for policy script to execute (5 seconds)...${NC}"
+echo -e "${YELLOW}Relay logs after EVENT test:${NC}"
+docker logs orly-policy-test 2>&1 | tail -10
+
+echo ""
+echo -e "${YELLOW}Step 10: Testing REQ message (read control)...${NC}"
+
+# Test REQ message
+./policytest -url "ws://localhost:$RELAY_PORT" -type req -kind 1 2>&1 || echo "REQ test completed"
+
+echo ""
+echo -e "${YELLOW}Relay logs after REQ test:${NC}"
+docker logs orly-policy-test 2>&1 | tail -10
+
+echo ""
+echo -e "${YELLOW}Step 11: Waiting for policy script to execute (5 seconds)...${NC}"
sleep 5
echo ""
-echo -e "${YELLOW}Step 10: Checking if cs-policy.js created output file...${NC}"
+echo -e "${YELLOW}Step 12: Checking if cs-policy.js created output file...${NC}"
# Check if the output file exists in the container
if docker exec orly-policy-test test -f /home/orly/cs-policy-output.txt; then
@@ -90,8 +94,26 @@ if docker exec orly-policy-test test -f /home/orly/cs-policy-output.txt; then
echo "Output file contents:"
docker exec orly-policy-test cat /home/orly/cs-policy-output.txt
echo ""
- echo -e "${GREEN}✓ Policy script is working correctly!${NC}"
- EXIT_CODE=0
+
+ # Check if we see both read and write access types
+ WRITE_COUNT=$(docker exec orly-policy-test cat /home/orly/cs-policy-output.txt | grep -c "Access: write" || echo "0")
+ READ_COUNT=$(docker exec orly-policy-test cat /home/orly/cs-policy-output.txt | grep -c "Access: read" || echo "0")
+
+ echo "Policy invocations:"
+ echo " - Write operations: $WRITE_COUNT"
+ echo " - Read operations: $READ_COUNT"
+ echo ""
+
+ if [ "$WRITE_COUNT" -gt 0 ] && [ "$READ_COUNT" -gt 0 ]; then
+ echo -e "${GREEN}✓ Policy script processed both write and read operations!${NC}"
+ EXIT_CODE=0
+ elif [ "$WRITE_COUNT" -gt 0 ]; then
+ echo -e "${YELLOW}⚠ Policy script only processed write operations (read operations may not have been tested)${NC}"
+ EXIT_CODE=0
+ else
+ echo -e "${YELLOW}⚠ Policy script is working but access types may not be logged correctly${NC}"
+ EXIT_CODE=0
+ fi
else
echo -e "${RED}✗ FAILURE: cs-policy-output.txt file not found!${NC}"
echo ""
@@ -101,7 +123,7 @@ else
fi
echo ""
-echo -e "${YELLOW}Step 11: Additional debugging info...${NC}"
+echo -e "${YELLOW}Step 13: Additional debugging info...${NC}"
echo "Files in /home/orly directory:"
docker exec orly-policy-test ls -la /home/orly/