feat: harden send file idempotency audit

This commit is contained in:
zhaoyilun
2026-07-10 23:02:25 +08:00
parent 1fd10b5df7
commit fab0f0f5c0
6 changed files with 566 additions and 22 deletions

View File

@@ -100,7 +100,7 @@ func main() {
ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
defer cancel()
for _, key := range []string{"ISPHERE_PACKET_LOG_FILE", "ISPHERE_PACKET_LOG_DIR", "ISPHERE_MSGLIB_SIDECAR_EXE", "ISPHERE_MSGLIB_SQLITE_DLL", "ISPHERE_MSGLIB_DB", "ISPHERE_MSGLIB_PASSWORD", "ISPHERE_SEND_FILE_ALLOWED_DIR"} {
for _, key := range []string{"ISPHERE_PACKET_LOG_FILE", "ISPHERE_PACKET_LOG_DIR", "ISPHERE_MSGLIB_SIDECAR_EXE", "ISPHERE_MSGLIB_SQLITE_DLL", "ISPHERE_MSGLIB_DB", "ISPHERE_MSGLIB_PASSWORD", "ISPHERE_SEND_FILE_ALLOWED_DIR", "ISPHERE_SEND_FILE_AUDIT_PATH", "ISPHERE_SEND_FILE_IDEMPOTENCY_PATH"} {
if err := os.Unsetenv(key); err != nil {
fail("server/env", err)
}
@@ -116,6 +116,12 @@ func main() {
if err := os.Setenv("ISPHERE_SEND_IDEMPOTENCY_PATH", filepath.Join(sendStateDir, "send-idempotency.jsonl")); err != nil {
fail("server/send_state", err)
}
if err := os.Setenv("ISPHERE_SEND_FILE_AUDIT_PATH", filepath.Join(sendStateDir, "send-file-preview.jsonl")); err != nil {
fail("server/send_file_state", err)
}
if err := os.Setenv("ISPHERE_SEND_FILE_IDEMPOTENCY_PATH", filepath.Join(sendStateDir, "send-file-idempotency.jsonl")); err != nil {
fail("server/send_file_state", err)
}
sendFileDir, err := os.MkdirTemp("", "isphere-send-file-*")
if err != nil {
fail("server/send_file_state", err)
@@ -235,7 +241,7 @@ func main() {
defaultGroupCount := verifySearchGroups(ctx, session, "project", "tools/call isphere_search_groups default", 0)
defaultFileCount := verifyReceiveFiles(ctx, session, "report", "tools/call isphere_receive_files default", 0, "", "")
sendPreviewOK, productionSendEnabled := verifySendMessagePreview(ctx, session)
sendFilePreviewOK, sendFileProductionEnabled := verifySendFilePreview(ctx, session, sendFilePath)
sendFilePreviewOK, sendFileProductionEnabled, sendFileDuplicateDetected, sendFileConflictBlocked := verifySendFilePreview(ctx, session, sendFilePath)
configuredReceiveCount, configuredContactCount, configuredGroupCount, configuredFileCount := verifyConfiguredReceiveSource(ctx)
configuredDirReceiveCount, configuredDirContactCount, configuredDirGroupCount, configuredDirFileCount := verifyConfiguredDirectorySource(ctx)
@@ -267,6 +273,8 @@ func main() {
"production_send_enabled": productionSendEnabled,
"send_file_preview_tool_present": sendFilePreviewOK,
"send_file_production_enabled": sendFileProductionEnabled,
"send_file_duplicate_detected": sendFileDuplicateDetected,
"send_file_conflict_blocked": sendFileConflictBlocked,
"msglib_configured": false,
}
encoded, _ := json.Marshal(result)
@@ -586,9 +594,9 @@ func verifySendMessagePreview(ctx context.Context, session *mcp.ClientSession) (
return true, false
}
func verifySendFilePreview(ctx context.Context, session *mcp.ClientSession, filePath string) (bool, bool) {
func verifySendFilePreview(ctx context.Context, session *mcp.ClientSession, filePath string) (bool, bool, bool, bool) {
fileHash := sha256FileHexForHarness(filePath)
callResult, err := session.CallTool(ctx, &mcp.CallToolParams{Name: "isphere_send_file", Arguments: map[string]any{
previewArgs := map[string]any{
"target_type": "direct",
"target_id": "sender@imopenfire1-lanzhou",
"file_path": filePath,
@@ -596,7 +604,8 @@ func verifySendFilePreview(ctx context.Context, session *mcp.ClientSession, file
"idempotency_key": "verify-file-preview-idempotency-key",
"execution_mode": "preview",
"preview": true,
}})
}
callResult, err := session.CallTool(ctx, &mcp.CallToolParams{Name: "isphere_send_file", Arguments: previewArgs})
if err != nil {
fail("tools/call isphere_send_file preview", err)
}
@@ -619,6 +628,62 @@ func verifySendFilePreview(ctx context.Context, session *mcp.ClientSession, file
fail("tools/call isphere_send_file preview", fmt.Errorf("unexpected send-file side effects: %s", encoded))
}
duplicateResult, err := session.CallTool(ctx, &mcp.CallToolParams{Name: "isphere_send_file", Arguments: previewArgs})
if err != nil {
fail("tools/call isphere_send_file duplicate", err)
}
if duplicateResult.IsError {
fail("tools/call isphere_send_file duplicate", fmt.Errorf("tool returned isError=true: %#v", duplicateResult.Content))
}
var duplicatePayload map[string]any
duplicateEncoded, _ := json.Marshal(duplicateResult.StructuredContent)
if err := json.Unmarshal(duplicateEncoded, &duplicatePayload); err != nil {
fail("tools/call isphere_send_file duplicate", fmt.Errorf("decode structuredContent %s: %w", duplicateEncoded, err))
}
duplicateIdempotency, ok := duplicatePayload["idempotency"].(map[string]any)
if !ok || duplicatePayload["ok"] != true || duplicatePayload["send_status"] != "planned" || duplicatePayload["real_send_attempted"] != false || duplicateIdempotency["duplicate_detected"] != true || duplicateIdempotency["conflict_detected"] != false {
fail("tools/call isphere_send_file duplicate", fmt.Errorf("duplicate idempotency mismatch: %s", duplicateEncoded))
}
conflictPath := filepath.Join(filepath.Dir(filePath), "redacted-conflict.txt")
if err := os.WriteFile(conflictPath, []byte("world"), 0o600); err != nil {
fail("tools/call isphere_send_file conflict_fixture", err)
}
conflictKey := "verify-file-conflict-idempotency-key"
if _, err := session.CallTool(ctx, &mcp.CallToolParams{Name: "isphere_send_file", Arguments: map[string]any{
"target_type": "direct",
"target_id": "sender@imopenfire1-lanzhou",
"file_path": filePath,
"file_sha256": fileHash,
"idempotency_key": conflictKey,
"execution_mode": "preview",
}}); err != nil {
fail("tools/call isphere_send_file conflict_first", err)
}
conflictResult, err := session.CallTool(ctx, &mcp.CallToolParams{Name: "isphere_send_file", Arguments: map[string]any{
"target_type": "direct",
"target_id": "sender@imopenfire1-lanzhou",
"file_path": conflictPath,
"file_sha256": sha256FileHexForHarness(conflictPath),
"idempotency_key": conflictKey,
"execution_mode": "preview",
}})
if err != nil {
fail("tools/call isphere_send_file conflict", err)
}
if conflictResult.IsError {
fail("tools/call isphere_send_file conflict", fmt.Errorf("tool returned isError=true: %#v", conflictResult.Content))
}
var conflictPayload map[string]any
conflictEncoded, _ := json.Marshal(conflictResult.StructuredContent)
if err := json.Unmarshal(conflictEncoded, &conflictPayload); err != nil {
fail("tools/call isphere_send_file conflict", fmt.Errorf("decode structuredContent %s: %w", conflictEncoded, err))
}
conflictIdempotency, ok := conflictPayload["idempotency"].(map[string]any)
if !ok || conflictPayload["ok"] != false || conflictPayload["send_status"] != "blocked" || conflictPayload["real_send_attempted"] != false || conflictIdempotency["duplicate_detected"] != false || conflictIdempotency["conflict_detected"] != true {
fail("tools/call isphere_send_file conflict", fmt.Errorf("conflict idempotency mismatch: %s", conflictEncoded))
}
productionResult, err := session.CallTool(ctx, &mcp.CallToolParams{Name: "isphere_send_file", Arguments: map[string]any{
"target_type": "direct",
"target_id": "sender@imopenfire1-lanzhou",
@@ -645,7 +710,7 @@ func verifySendFilePreview(ctx context.Context, session *mcp.ClientSession, file
if !ok || productionSideEffects["sent_file"] != false || productionSideEffects["uploaded_file"] != false || productionSideEffects["captured_network"] != false {
fail("tools/call isphere_send_file production_blocked", fmt.Errorf("unexpected production file side effects: %s", productionEncoded))
}
return true, false
return true, false, true, true
}
func verifySearchContacts(ctx context.Context, session *mcp.ClientSession, query string, step string, wantCount int) int {
@@ -856,6 +921,8 @@ func fail(step string, err error) {
Assert-True ($script:harnessJson.production_send_enabled -eq $false) "SDK harness production_send_enabled mismatch: $($script:harnessJson.production_send_enabled)"
Assert-True ($script:harnessJson.send_file_preview_tool_present -eq $true) "SDK harness send_file_preview_tool_present mismatch: $($script:harnessJson.send_file_preview_tool_present)"
Assert-True ($script:harnessJson.send_file_production_enabled -eq $false) "SDK harness send_file_production_enabled mismatch: $($script:harnessJson.send_file_production_enabled)"
Assert-True ($script:harnessJson.send_file_duplicate_detected -eq $true) "SDK harness send_file_duplicate_detected mismatch: $($script:harnessJson.send_file_duplicate_detected)"
Assert-True ($script:harnessJson.send_file_conflict_blocked -eq $true) "SDK harness send_file_conflict_blocked mismatch: $($script:harnessJson.send_file_conflict_blocked)"
[ordered]@{
ok = $true
@@ -880,6 +947,8 @@ func fail(step string, err error) {
production_send_enabled = $script:harnessJson.production_send_enabled
send_file_preview_tool_present = $script:harnessJson.send_file_preview_tool_present
send_file_production_enabled = $script:harnessJson.send_file_production_enabled
send_file_duplicate_detected = $script:harnessJson.send_file_duplicate_detected
send_file_conflict_blocked = $script:harnessJson.send_file_conflict_blocked
packet_log_file_configured = $script:harnessJson.packet_log_file_configured
packet_log_dir_configured = $script:harnessJson.packet_log_dir_configured
python_runtime_required = $false