f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
104 lines
3.9 KiB
Go
104 lines
3.9 KiB
Go
package sync
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"go.kenn.io/agentsview/internal/testjsonl"
|
|
)
|
|
|
|
// TestSyncPersistsSecretFindings verifies that SyncAll scans session
|
|
// content and persists secret findings + secret_leak_count after a sync.
|
|
// The session takes the APPEND branch of writeBatch (fresh Claude session),
|
|
// validating Edit A's append path end-to-end.
|
|
func TestSyncPersistsSecretFindings(t *testing.T) {
|
|
fx := newEngineFixture(t)
|
|
|
|
// Build a session with two user messages; the second carries a
|
|
// definite AWS access key secret (aws-access-key rule).
|
|
content := testjsonl.NewSessionBuilder().
|
|
AddClaudeUser("2024-01-01T00:00:00Z", "hello, please help me").
|
|
AddClaudeAssistant("2024-01-01T00:00:01Z", "sure, what do you need?").
|
|
AddRaw(testjsonl.ClaudeAssistantJSON(
|
|
[]map[string]any{{
|
|
"type": "tool_use",
|
|
"id": "toolu_aws1",
|
|
"name": "Bash",
|
|
"input": map[string]string{
|
|
"command": "echo hi",
|
|
},
|
|
}},
|
|
"2024-01-01T00:00:02Z",
|
|
)).
|
|
AddRaw(testjsonl.ClaudeToolResultUserJSON(
|
|
"toolu_aws1",
|
|
"AWS_ACCESS_KEY_ID=AKIA7QHWN2DKR4FYPLJM found in env",
|
|
"2024-01-01T00:00:03Z",
|
|
)).
|
|
AddClaudeUser("2024-01-01T00:00:04Z", "that key AKIA7QHWN2DKR4FYPLJM is mine").
|
|
String()
|
|
|
|
filename := "secret-session.jsonl"
|
|
path := filepath.Join(fx.claudeDir, "proj", filename)
|
|
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
|
|
require.NoError(t, os.WriteFile(path, []byte(content), 0o644))
|
|
|
|
stats := fx.engine.SyncAll(context.Background(), nil)
|
|
require.NotZero(t, stats.Synced, "expected Synced > 0, got %+v", stats)
|
|
|
|
sessionID := fx.sessionIDFor(t, path)
|
|
|
|
sess, err := fx.db.GetSession(context.Background(), sessionID)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, sess, "session %q not found after SyncAll", sessionID)
|
|
assert.GreaterOrEqual(t, sess.SecretLeakCount, 1, "SecretLeakCount = %d, want >= 1", sess.SecretLeakCount)
|
|
|
|
findings, err := fx.db.SessionSecretFindings(context.Background(), sessionID)
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, findings, "expected non-empty findings slice, got 0")
|
|
|
|
var sawAWS, sawToolOutput bool
|
|
for _, f := range findings {
|
|
if f.RuleName != "aws-access-key" {
|
|
continue
|
|
}
|
|
sawAWS = true
|
|
if f.LocationKind == "tool_result" || f.LocationKind == "tool_result_event" {
|
|
sawToolOutput = true
|
|
}
|
|
}
|
|
assert.True(t, sawAWS, "no aws-access-key finding in %+v", findings)
|
|
// Pin the real use case: the secret captured in tool output must be
|
|
// detected, not only the copy in the user message.
|
|
assert.True(t, sawToolOutput, "no aws-access-key finding in tool output: %+v", findings)
|
|
}
|
|
|
|
// TestSyncNoSecretsLeavesZero verifies a clean session persists no findings
|
|
// and a zero secret_leak_count, exercising the empty-findings path through
|
|
// the sync write (replaceSecretFindingsTx deletes with nothing to insert).
|
|
func TestSyncNoSecretsLeavesZero(t *testing.T) {
|
|
fx := newEngineFixture(t)
|
|
content := testjsonl.NewSessionBuilder().
|
|
AddClaudeUser("2024-01-01T00:00:00Z", "hello, please help me").
|
|
AddClaudeAssistant("2024-01-01T00:00:01Z", "sure, happy to help").
|
|
AddClaudeUser("2024-01-01T00:00:02Z", "thanks, that works").
|
|
String()
|
|
path := filepath.Join(fx.claudeDir, "proj", "clean-session.jsonl")
|
|
require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755))
|
|
require.NoError(t, os.WriteFile(path, []byte(content), 0o644))
|
|
stats := fx.engine.SyncAll(context.Background(), nil)
|
|
require.NotZero(t, stats.Synced, "expected Synced > 0, got %+v", stats)
|
|
sessionID := fx.sessionIDFor(t, path)
|
|
sess, err := fx.db.GetSession(context.Background(), sessionID)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, sess, "session %q not found after SyncAll", sessionID)
|
|
assert.Equal(t, 0, sess.SecretLeakCount)
|
|
findings, err := fx.db.SessionSecretFindings(context.Background(), sessionID)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, findings, "expected 0 findings, got %d: %+v", len(findings), findings)
|
|
}
|