mirror of
https://github.com/BaaRai/ARTEX.git
synced 2026-11-13 11:40:53 +08:00
The quote x7 is now rejected as too short before the store is read, so the test never reached the not_assessed path. Use a quote long enough to pass the minimum; the store removal is unchanged.
179 lines
7.0 KiB
Go
179 lines
7.0 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http/httptest"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/Autumn-27/artex/db"
|
|
actool "github.com/Autumn-27/norma/tool"
|
|
)
|
|
|
|
const verificationBody = "HTTP/1.1 200 OK\nuser admin:\tx7\nend of dump"
|
|
|
|
func TestNormalizeEvidenceText(t *testing.T) {
|
|
cases := map[string]string{
|
|
" a\t\tb\r\n c ": "a b c",
|
|
"Admin User": "Admin User", // case is kept
|
|
"": "",
|
|
}
|
|
for in, want := range cases {
|
|
if got := normalizeEvidenceText(in); got != want {
|
|
t.Fatalf("normalize(%q) = %q, want %q", in, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestShortQuoteCapsAtSixtyRunes(t *testing.T) {
|
|
long := strings.Repeat("é", 100)
|
|
if got := []rune(shortQuote(long)); len(got) != verificationNoteQuoteLen {
|
|
t.Fatalf("shortQuote kept %d runes, want %d", len(got), verificationNoteQuoteLen)
|
|
}
|
|
}
|
|
|
|
// verificationFixture returns a server with one finding whose traffic "a" is
|
|
// bound with the given role and whose response body is verificationBody.
|
|
func verificationFixture(t *testing.T, role string) (*Server, *db.RecordedFinding, func(string, string, string) *httptest.ResponseRecorder) {
|
|
t.Helper()
|
|
s, f, req := trafficEvidenceServer(t)
|
|
seedServerEvidenceFlow(t, s, "a", []byte(verificationBody))
|
|
if role != "" {
|
|
base := fmt.Sprintf("/api/exploration/findings/%d/traffic", f.FindingID)
|
|
w := req("POST", base, fmt.Sprintf(`{"traffic_refs":[{"traffic_id":"a","role":%q}]}`, role))
|
|
if w.Code != 200 {
|
|
t.Fatalf("bind: %d %s", w.Code, w.Body)
|
|
}
|
|
}
|
|
return s, f, req
|
|
}
|
|
|
|
// updateReport calls update_finding_report with the node id, as the agent does.
|
|
func updateReport(t *testing.T, s *Server, nodeID int64, extra string) (string, bool) {
|
|
t.Helper()
|
|
in := fmt.Sprintf(`{"finding_id":%d,"report":"# report","evidence_version":null%s}`, nodeID, extra)
|
|
res, err := s.toolUpdateFindingReport().Call(context.Background(), json.RawMessage(in), &actool.ToolContext{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return res.Flatten(), res.IsError
|
|
}
|
|
|
|
func storedVerification(t *testing.T, s *Server, findingID int64) (status string, poe int, note, report string) {
|
|
t.Helper()
|
|
if err := s.m.pg.QueryRow(`SELECT verification_status, poe_level, verification_note, report FROM findings WHERE id=$1`, findingID).
|
|
Scan(&status, &poe, ¬e, &report); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return
|
|
}
|
|
|
|
func TestVerificationNoBindingPOE4Unverified(t *testing.T) {
|
|
s, f, _ := verificationFixture(t, "")
|
|
msg, isErr := updateReport(t, s, f.NodeID, `,"poe_level":4,"evidence_quotes":["x7"]`)
|
|
if isErr || !strings.Contains(msg, "verification: unverified - no proof traffic bound") {
|
|
t.Fatalf("unexpected result: err=%v %q", isErr, msg)
|
|
}
|
|
status, poe, note, report := storedVerification(t, s, f.FindingID)
|
|
if status != db.VerificationUnverified || poe != 4 || note != "no proof traffic bound" || report == "" {
|
|
t.Fatalf("stored: status=%s poe=%d note=%q report=%q", status, poe, note, report)
|
|
}
|
|
}
|
|
|
|
func TestVerificationProofPOE3MissingQuoteUnverified(t *testing.T) {
|
|
s, f, _ := verificationFixture(t, "proof")
|
|
msg, isErr := updateReport(t, s, f.NodeID, `,"poe_level":3,"evidence_quotes":["secret=999"]`)
|
|
if isErr || !strings.Contains(msg, "warning:") {
|
|
t.Fatalf("unexpected result: err=%v %q", isErr, msg)
|
|
}
|
|
status, _, note, report := storedVerification(t, s, f.FindingID)
|
|
if status != db.VerificationUnverified || !strings.HasPrefix(note, "quote not found in bound evidence: secret=999") || report == "" {
|
|
t.Fatalf("stored: status=%s note=%q report=%q", status, note, report)
|
|
}
|
|
}
|
|
|
|
func TestVerificationProofPOE3ExactQuoteVerified(t *testing.T) {
|
|
s, f, _ := verificationFixture(t, "verification")
|
|
// The quote differs from the body only in whitespace runs: it must still match.
|
|
msg, isErr := updateReport(t, s, f.NodeID, `,"poe_level":3,"evidence_quotes":["user admin: x7"]`)
|
|
if isErr || !strings.Contains(msg, "verification: verified (POE L3)") {
|
|
t.Fatalf("unexpected result: err=%v %q", isErr, msg)
|
|
}
|
|
status, poe, note, _ := storedVerification(t, s, f.FindingID)
|
|
if status != db.VerificationVerified || poe != 3 || note != "" {
|
|
t.Fatalf("stored: status=%s poe=%d note=%q", status, poe, note)
|
|
}
|
|
}
|
|
|
|
func TestVerificationShortQuoteUnverified(t *testing.T) {
|
|
s, f, _ := verificationFixture(t, "proof")
|
|
// "x7" is present in the body but below verificationMinQuoteRunes: it must not count.
|
|
msg, isErr := updateReport(t, s, f.NodeID, `,"poe_level":3,"evidence_quotes":["x7"]`)
|
|
if isErr || !strings.Contains(msg, "verification: unverified - evidence quote too short") {
|
|
t.Fatalf("unexpected result: err=%v %q", isErr, msg)
|
|
}
|
|
status, _, note, _ := storedVerification(t, s, f.FindingID)
|
|
if status != db.VerificationUnverified || note != "evidence quote too short" {
|
|
t.Fatalf("stored: status=%s note=%q", status, note)
|
|
}
|
|
}
|
|
|
|
func TestVerificationOldStyleCallUnverifiedWithoutError(t *testing.T) {
|
|
s, f, _ := verificationFixture(t, "proof")
|
|
msg, isErr := updateReport(t, s, f.NodeID, "")
|
|
if isErr || !strings.Contains(msg, "verification: unverified") {
|
|
t.Fatalf("unexpected result: err=%v %q", isErr, msg)
|
|
}
|
|
status, poe, _, report := storedVerification(t, s, f.FindingID)
|
|
if status != db.VerificationUnverified || poe != 0 || report == "" {
|
|
t.Fatalf("stored: status=%s poe=%d report=%q", status, poe, report)
|
|
}
|
|
}
|
|
|
|
func TestVerificationUnreadableStoreNotAssessedReportSaved(t *testing.T) {
|
|
s, f, _ := verificationFixture(t, "proof")
|
|
// Remove the blobs behind the bound snapshot: the evidence store cannot open the body.
|
|
if err := os.RemoveAll(filepath.Join(s.m.dir, "evidence")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
msg, isErr := updateReport(t, s, f.NodeID, `,"poe_level":3,"evidence_quotes":["user admin: x7"]`)
|
|
if isErr || !strings.Contains(msg, "verification: not_assessed") {
|
|
t.Fatalf("unexpected result: err=%v %q", isErr, msg)
|
|
}
|
|
status, _, note, report := storedVerification(t, s, f.FindingID)
|
|
if status != db.VerificationNotAssessed || !strings.HasPrefix(note, "evidence body unreadable") || report == "" {
|
|
t.Fatalf("stored: status=%s note=%q report=%q", status, note, report)
|
|
}
|
|
}
|
|
|
|
func TestVerificationRejectsOutOfRangePOE(t *testing.T) {
|
|
s, f, _ := verificationFixture(t, "proof")
|
|
msg, isErr := updateReport(t, s, f.NodeID, `,"poe_level":9`)
|
|
if !isErr || !strings.Contains(msg, "invalid poe_level 9") {
|
|
t.Fatalf("unexpected result: err=%v %q", isErr, msg)
|
|
}
|
|
if _, _, _, report := storedVerification(t, s, f.FindingID); report != "" {
|
|
t.Fatal("report must not be written when the input is rejected")
|
|
}
|
|
}
|
|
|
|
func TestVerificationColumnsExistWithHonestDefaults(t *testing.T) {
|
|
s, f, _ := trafficEvidenceServer(t)
|
|
var n int
|
|
if err := s.m.pg.QueryRow(`SELECT count(*) FROM information_schema.columns
|
|
WHERE table_name='findings' AND column_name IN ('poe_level','verification_status','verification_note')`).Scan(&n); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if n != 3 {
|
|
t.Fatalf("expected 3 verification columns, found %d", n)
|
|
}
|
|
status, poe, note, _ := storedVerification(t, s, f.FindingID)
|
|
if status != db.VerificationUnverified || poe != 0 || note != "" {
|
|
t.Fatalf("new row: status=%s poe=%d note=%q", status, poe, note)
|
|
}
|
|
}
|