Commit Graph

4 Commits

Author SHA1 Message Date
Claude b147694e31 Härdning av hela TÜV-revisionen
Tio av tolv fynd stängda, två reducerade med skälen utskrivna. Varje
stängning prövas av integrationssviten mot riktig Postgres — 144
kontroller, upp från 100.

T-1  Händelsenyckeln är nu (arende_id, id). Det finns inget delat
     namnrum kvar att ockupera, så attacken saknar yta i stället för att
     vara mildrad. Inom ett ärende bedöms en kollision på klientens
     avtryck — en hash av det kanoniserade innehåll klienten skickade,
     taget före serverns egna fält och före krypteringen, eftersom
     varken raden eller nyttolasten går att jämföra. Identiskt innehåll
     är fortfarande idempotent; samma id med annat innehåll ger 409 och
     skriver ingenting alls, inte heller resten av satsen. Samma attack
     en nivå upp — ärende-id är lika förutsägbart — stängs separat: ett
     id som ägs av en annan organisation ger 409 i stället för att tyst
     låta bli, vilket tidigare lämnade offrets ärende oskapat och varje
     senare synk svarande 404.

T-2  Mätdonet slås upp i registret. Beteckning och kalibrering HÄRLEDS
     därifrån och skriver över det klienten skickade. Okänt mätdon ger
     400. Utgånget avvisas inte — mätningen gjordes — men registrets
     datum följer med, så graderingen faller på registrets uppgift. Ett
     mätvärde utan mätdon får sina påstådda uppgifter borttagna.

T-3  Reducerad, inte stängd. Personnycklarna kuverteras under en
     huvudnyckel utanför databasen. En återställd dump ger nycklar som
     inte öppnas — verifierat genom att starta om utan huvudnyckeln. Vad
     som återstår står utskrivet: en backup tagen FÖRE en radering, plus
     huvudnyckeln, återställer fortfarande uppgifterna.

Därtill: gallringen verkställs nu av ett eget jobb och grupperar på det
blindade fordonsindexet så att en delad nyckel inte gallras för tidigt
(T-4) · AI-avlästa mätvärden bär härkomst (T-5) · bcrypt kostnad 12
(T-6) · åtkomstloggen och raderingsregistret är append-only, med en
smalare regel för personnycklarna som måste kunna förstöras (T-7) ·
åtkomstloggen dokumenterad (T-8) · ett externt regelpaket utan signatur
spärrar avslut (T-9) · CORS faller inte längre öppet (T-10) · exp krävs
i token (T-11) · react-router 7 (T-12, med den kvarvarande avvikelsen
motiverad).

Den motspelande hyresgästen som revisionen efterlyste finns nu som
testform och körs i CI.

---- Vad härdningen själv avslöjade -----------------------------------

Två av rättelserna var kortvarigt fel på samma sätt som fynden, och
bägge fångades bara av att jag försökte bevisa dem:

  T-7-testet var grönt mot en TOM tabell. En radnivåtrigger har inga
  rader att fyra på, så delete lyckades och kontrollen mätte ingenting.

  T-6-testet påstod anropet, inte kostnaden. toContain("gen_salt('bf')")
  hade accepterat kostnad 6 för evigt — och gjorde det, så länge det
  fanns.

Bägge är mönstret revisionen namngav: en kontroll som är riktig i sina
egna termer och oprövad vid sin gräns. Det gäller tester lika mycket som
kod.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
2026-08-06 13:05:16 +00:00
Claude d1b361573e Faktureringens serverdel — och två fel den grävde fram
Fakturering fanns som modell, vy och tester men aldrig som något en
server kunde utfärda. Nu finns tabellerna, vägarna och gränsen mellan
kund och utfärdare.

Beloppet tas aldrig emot. Det härleds ur organisationens faktiska
tillstånd — de konton som verkligen kan logga in, de moduler som
verkligen är påslagna — och ett anrop som ändå skickar rader eller
totalt avvisas med 400 i stället för att tigas ihjäl. Samma hållning som
mot okända fält i händelseschemat.

Fakturaraden är oföränderlig, skyddad av samma trigger som loggen. Det
får en följd som är lätt att missa: "betald" kan då inte vara en kolumn
som uppdateras. Betalningen är en egen händelse och statusen en
projektion av händelserna. En felaktig faktura rättas inte heller — den
bemöts av en kreditfaktura med omvänt tecken och ett granskbart skäl.

Utfärdaren är inte en användare. Ingen av rollerna i en verkstad är
motpart i avtalet, så en kunds administratör kan varken utfärda sin egen
faktura eller bokföra den som betald; utfärdandet kräver en egen nyckel,
och utan den i miljön utfärdas ingenting alls. Nummerserien är utan
luckor — en sequence hade varit billigare men lämnar hål vid rollback,
och ett underlag med hål i är en lista.

---- Vad som föll ut när sviten faktiskt kördes ------------------------

integrationstest.sh fanns men låg utanför CI, och den föll på andra
raden — i kod som inte hade med fakturering att göra:

C-7  Append-only-triggern på felsokning_arenden förbjöd ALL update. Två
     av radens kolumner är härledda efteråt: gallringsdatumet vid avslut
     och det blindade fordonsindexet. Alltså föll varje avslut med 500,
     efter att kvalitetsgrinden redan godkänt ärendet. Skyddet är nu
     kolumnvis: identitet och ursprung är fortfarande låsta, radering
     fortfarande omöjlig, men de fält systemet självt härleder får
     skrivas.

C-8  Fordonshistoriken sökte i klartext efter en identifierare som
     krypteras i vila. Jämförelsen kunde aldrig träffa: historiken
     svarade tomt på varje fordon, med 200. Det blindade indexet fanns
     just för den frågan och var aldrig inkopplat.

Bägge ligger i backenden till produktens centrala löfte — att ett
avslutat ärende är ett varaktigt underlag — och ingen av dem kunde synas
i en grön enhetssvit, eftersom ingen av dem kan falla utan en databas.
Sviten är därför ett eget CI-jobb nu.

Bevisad: 364 enhetstester, 100+ integrationskontroller mot riktig
Postgres, genomgången 4/4 ärenden. Spärren mot angivet belopp
mutationstestad — borttagen ger den 201 i stället för 400.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
2026-08-06 10:54:31 +00:00
Claude cd6b2caca5 Sammanfattning och slutsats i rapport och Live Share; API dokumenterat
Sammanfattningen ligger först i både kundrapporten och Live Share.
Mottagaren är oftast inte tekniker — det är kunden, en
försäkringshandläggare eller en flottansvarig — och de ska få bilden på
fem sekunder och sedan kunna gå djupare, inte tvärtom.

I Live Share härleds den ur det NIVÅFILTRERADE underlaget. Det betyder
att sammanfattningen aldrig kan avslöja något som delningsnivån döljer:
filtret ligger före projektionen, inte efter.

Slutsatsen visas som ett eget avsnitt före underlaget. En handläggare
läser skälet först och kontrollerar det sedan — den ordningen är hela
poängen med att fältet finns.

Sju nya vägar dokumenterade i OpenAPI och låsta av paritetstestet:
sammanfattning, protokollinläsning, statistik, integrationskategorier,
prenumerationer, radering och mätdon. Ett API som inte är dokumenterat
är inte ett API någon kan koppla in sig mot — och specen valideras
maskinellt så dokumentationen inte kan glida från servern.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
2026-08-05 15:55:31 +00:00
Claude 301c477e25 Merge main: Guidad Felsökning flyttar ur roten till felsokning/
Main är sedan den här grenen skapades en helt annan produkt — Semantika,
en mobilapp med egen CDK-infrastruktur. Den äger nu repots rot: en
npm-workspaces-monorepo med apps/mobile, services/api och infra.

För att båda ska rymmas i samma repo flyttar Guidad Felsökning in i en
egen katalog i stället för att göra anspråk på roten:

  felsokning/app        webbklienten (Vite, egen package.json och
                        eslint-/vitest-konfiguration)
  felsokning/services   plattformstjänsten och AI-orkestern
  felsokning/infra      Terraform och databasschemat
  felsokning/docs       vision, moduler, drift
  felsokning/supabase   edge-funktion och migrationer

Merge:n hade tagit bort 128 filer som Guidad Felsökning bygger på —
värdapplikationens komponenter, Supabase-klienten, tillgångar — eftersom
main raderat dem och den här grenen inte råkat ändra just dem. De är
återställda på sin nya plats. Utan dem gick varken bygget eller
testerna: ai.ts och synk.ts importerar Supabase-klienten.

Semantikas rotfiler är orörda: package.json, eslint.config.js och
.github/workflows/ är deras. Guidad Felsökning har egna motsvarigheter i
sin katalog.

CI flyttar samtidigt från GitHub Actions till .gitea/workflows — samma
syntax, egna runners. .github/workflows/ tillhör Semantika härefter.

Verifierat på den nya platsen: 96 vitest-tester, typkontroll, eslint på
både klient och tjänster, bygge, och integrationstest mot riktig Postgres.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
2026-08-04 12:22:49 +00:00