1ba5aaaefa301705059be9ca422e62b4a2b9cb63
8 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1ba5aaaefa |
Härdning efter panelgranskningen: kedjad logg, förseglat avslut, härledd säkerhetsnivå
Fyra av granskningens fynd åtgärdade, i bevisvärdesordning. HASHKEDJAN (ALVA-SPEC-070). Triggrar skyddar loggen mot applikationen, inte mot den som äger databasen — det var granskningens allvarligaste invändning mot ett system vars hela värde är bevisvärde. Varje händelse bär nu en hash av sitt innehåll och föregående händelses hash, beräknad av servern vid insättningen. All skrivning går genom en enda kedjande funktion; en händelse vid sidan av kedjan är ett hål i beviset, så den bekväma vägen förbi finns inte. Digest tas över den LAGRADE händelsen, efter kryptering: verifieringen ska kunna räkna om den ur databasen för all framtid, och krypto-shredding förstör nycklar, inte rader, så kedjan överlever en radering. Radlås per ärende hindrar att två samtidiga batchar forkar kedjan — en falsk larmande verifiering avfärdas snart som trasig, och då är den värdelös. Integrationstestet provar hotmodellen ordagrant: triggern släpps, en rad ändras med full databasbehörighet, triggern återskapas. Verifieringen pekar ut raden — inte bara att något är fel, utan vilken. FÖRSEGLINGEN. Avslut skriver kedjans rot och en HMAC med en nyckel som aldrig finns i databasen, i samma transaktion som avslutshändelsen. Den som räknar om hela kedjan efter sin ändring stoppas av att förseglingen inte går att räkna om utan nyckeln. Engångs: triggern vägrar ändra en satt försegling. Svaret säger vad det bevisar och inte — innehållet är oförändrat sedan mottagandet, ingenting om tiden före, ingenting om sanningshalten. Den texten följer med in i varje rapport som citerar svaret, för det är precis den skillnad en motpartsjurist annars hittar. SIGNATUREN. Fältet hette signatur men var teknikerns egen text — det inbjöd en jurist att tro något som inte gällde. Det skrivs nu ur verifierad token som övriga härkomstfält och intygar exakt vad det kan intyga: vem som var inloggad när avslutet togs emot. SÄKERHETSNIVÅN (ALVA-SPEC-071). Var teknikerns fria val — ett självskattat värde som ser ut som en mätning. Nu ett tak härlett ur underlaget: hög kräver reproducerat symptom OCH spårbart mätvärde ur mätdonsregistret; enbart observationer bär inte ens medel. Teknikern kan sänka men aldrig höja — asymmetrin är poängen, ärlig osäkerhet är information. Grinden spärrar påståenden över taket på alla tio språken, och gränssnittet visar taket medan arbetet pågår i stället för att spara beskedet till avslutsknappen. "Delvis reproducerat" bär inte hög: delvis är ett annat ord för att felet inte är förstått. Taket bet direkt i två av våra egna testfixturer som påstod hög utan spårbart mätdon — vilket är regeln som fungerar, inte testet som är fel. Genomgången avslöjade följdkravet: vid medel/låg kräver panelen att teknikern anger vilka ytterligare kontroller som skulle stärka bedömningen, och det fältet fylls nu i som en tekniker skulle. Kvar ur granskningens lista, medvetet: extern förankring (RFC 3161), klienthashat foto vid upptagning, gränsvärden som data, OIDC/SAML. 766 tester, 200 integrationskontroller mot riktig Postgres — inklusive sabotage som databasägare — genomgång 4/4, portalspärr, typkontroll, lint och artefaktmätning gröna. Utgåva 3.3, API-specen uppdaterad, åtgärderna bokförda i panelrapportens bilaga A. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
8aae3a2a97 |
Regelverk, ärendetyper och säkerhetssvar på engelska — med arv där data byter språk
ecm.ts bar regelverkets rådtexter: undantagsorsaker, orsakskategorier, underlagskällor, evidensgrader och de meningar som säger vad som fattas. Cirka hundra strängar, nu på engelska tillsammans med serverns ecm-regler.json — klienten och regelpaketet måste säga samma sak, annars är det inte samma regel. Tre av dem var inte text utan DATA, och där gjordes bytet med arv i stället för rakt av. ÄRENDETYPEN ligger i loggen och slår upp regelpaketets extra krav. Ett gammalt garantiärende vars typ heter "Garanti" hade inte matchat "Warranty", fått noll extra krav, och släppts igenom grinden med FÄRRE krav än när det öppnades — utan att någonting i gränssnittet visade det. ARENDETYP_ARV normaliserar typen vid uppslaget, aldrig i loggen. GALLRINGSTIDEN slås upp på samma nyckel, och det var värre. Ett garantiärende hade fallit ur tabellen och tagit standardtiden 36 månader i stället för 120 — alltså raderat garantiunderlaget sju år i förtid, tyst och oåterkalleligt. Ett uppslag som missar ska ge det försiktigaste utfallet; här gav det det farligaste. Två tester låser det. SÄKERHETSSPÄRREN avgörs av en textsträng. När knappen bytte från "Ja" till "Yes" gick klient och server isär: klienten skrev "Yes", servern jämförde mot "Ja". Utfallet blev fail-closed — högvoltsärenden gick inte att avsluta alls — vilket är rätt riktning för ett fel att falla åt, men det var en slump och inte en konstruktion. `arJakande` känner igen ett ja på alla språken. Att en säkerhetsspärr avgörs av fritext är i sig felet; rätt lösning är ett booleskt fält, och att arvet står synligt i koden är en påminnelse om det. Pre-diagnostikens kvitteringar känns igen på sin text och har fått samma behandling: gamla ärenden hade annars visat en ogjord kontroll för arbete som faktiskt utförts, och teknikern hade kvitterat en gång till — två kvitteringar för samma sak i en append-only logg. Genomgången drev gränssnittet på svenska knappnamn och gick sönder på rätt sätt: den hittade "+ Dokumentera felorsak", "Registrera besked" och kundkanalerna "Telefon"/"E-post", som alla missats av de tidigare svepen. Fyra av fyra ärenden avslutas igen. 609 tester, 186 integrationskontroller mot riktig Postgres, genomgång, portalspärr, typkontroll och lint gröna. Mutationsprövat: utan ARENDETYP_ARV faller två tester, utan `arJakande` ett. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
322d228c80 |
ALVA-RULE-200 gällde bara på svenska — nu på tio språk
Slutsatsgranskningen avvisar icke-svar med en ordlista: "klart", "ok", "trasig". Listan var enbart svensk. En tysk tekniker som skrev "erledigt" i motiveringsfältet passerade filtret — inte därför att texten dög, utan därför att filtret bara kände igen "klart". Grinden släppte igenom avslutet och rapporten såg fullständig ut. Det var alltså inte en översättningsbrist utan ett hål i den regel som är hela produktens värde, och hålet fanns på varje marknad utom en. Samma sak åt andra hållet: orsaksorden som visar att en text innehåller ett resonemang var svenska, liksom evidensorden. Engelskans "photo" innehåller inte "foto", så "the photo shows the arcing at pin 14" räknades inte som en hänvisning till underlag. Följden hade varit värre än ett släppt avslut — en korrekt skriven motivering hade nekats, och en spärr som nekar rätt svar är en spärr man lär sig kringgå. Listorna ligger nu i sprak/ord.mjs, per språk, och slås ihop till en union i stället för att väljas efter inställning. Ett icke-svar är ett icke-svar oavsett språk — jämförelsen görs mot hela fältet, så en tekniker som skriver på ett annat språk än organisationens ska inte kunna passera på den vägen. Orsaks- och evidensorden går åt motsatt håll: unionen gör kontrollen mer tillåtande, aldrig strängare, och det är rätt riktning att fela åt. Medvetet utanför listorna: "inget", "keine", "none". Att ingenting kvarstår osäkert är ett giltigt och ofta korrekt svar; att göra det till ett icke-svar hade tvingat fram utfyllnad, och utfyllnad är sämre än ett kort sant svar. Ett test låser det. Bristernas texter kommer nu ur katalogen och bär sin nyckel, precis som grindens hinder. Fältnamnet översätts med resten av meningen — "Begründung fehlt.", inte "Motivering fehlt.". Mutationsprövat: med listan återställd till enbart svenska föll nio tester, ett per språk. Svenska och engelska är genomgångna; övriga listor är en första uppsättning och står som ogranskade i filhuvudet, av samma skäl som `granskat` i SPRAK — de är metodik, inte gränssnitt. 597 tester, 186 integrationskontroller, typkontroll, lint och genomgång gröna. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
be10704349 |
Grinden talar organisationens språk — engelska som standard
Kvalitetsgrinden formulerade sina hinder på svenska, hårdkodat. I en produkt där engelska är standardspråket betyder det att en spärr kan neka ett avslut på ett språk teknikern inte läser. Det är en spärr utan väg förbi: teknikern ser att systemet vägrar men inte varför, och den enda utvägen blir att ringa någon. Det är då regeln slutar vara en regel och blir ett hinder man lär sig kringgå. Varje hinder bär nu både nyckel och färdig text. Nyckeln därför att en klient som byter språk ska kunna rendera om utan att fråga servern; texten därför att ett hinder som hamnar i en logg eller en PDF måste gå att läsa utan katalogen. Hindrets id och nyckel är språkoberoende — det är bara texten som byter. Språket är organisationens, inte användarens. En verkstad i Tyskland med en polsk tekniker ska ha ett gemensamt dokumentationsspråk; rapporten ska inte byta språk beroende på vem som råkade stänga ärendet. Det lagras i organisationens inställningar, och en felstavad landskod blir engelska i stället för ett fel — språkinställningen är en etikett och ska inte hindra någon från att spara sina objekttyper. SPARRFRAGOR bär nu nycklar i stället för svensk text. Testet som jämför serverns lista med klientens jämför nycklar, så listorna kan fortfarande inte glida isär. Två tester prövade svenska ord i grindens utdata och gick sönder, vilket var rätt av dem. De prövar nu nyckeln — texten är översatt och byter språk med organisationen, och att pröva svenska ord hade gjort dem till tester av vilket språk som råkar vara standard. Ett nytt test prövar det som faktiskt betyder något: samma id, samma nyckel, olika text. Integrationstestet följer språket hela vägen — organisationens inställning, genom servern, ut i det 409-svar som nekar avslutet: engelska som standard, tyska efter byte, engelska igen vid okänd kod. 582 tester, 186 integrationskontroller mot riktig Postgres, genomgång och portalspärr gröna. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
ae5acbed92 |
Fotograferad mätarställning, versionshistorik och tysk kravbild i foten
---- Mätarställningen ska fotograferas ---------------------------------- Klienten krävde redan fotot: värdefältet visas först efter bilden. Men GRINDEN gjorde det inte, och en regel som bara finns i gränssnittet är en vana — inte en spärr. Det är samma förhållande som QUALITET C-2 gällde, fast åt andra hållet: här var servern svagare, vilket är värre, eftersom servern är den auktoritativa. Skillnaden är hela uppgiftens bevisvärde. En inskriven siffra är teknikerns påstående om vad som stod på mätaren; ett foto visar vad som stod där. Mätarställningen avgör garanti- och försäkringsfrågor i efterhand och är den enda uppgift i ärendet någon kan ha ett intresse av att skriva fel. Evidensgraderingen följer nu med: fotograferad ger E2, inskriven ger E1 och märks som inskriven i rapporten. Undantag finns och måste finnas — slocknad display, oåtkomlig timräknare — men kräver motivering, som det nekade historiksvaret. ---- Versionen räknades aldrig upp -------------------------------------- Systemet påstod "ALVA 1.0" efter tre revisioner, en fakturamodul och en stängd portal. Versionen sa alltså ingenting om vad man fick. Historiken är HÄRLEDD UR VAD SOM FAKTISKT LEVERERATS och varje post pekar på sin commit. En utgåva som inte går att peka på hör inte hemma i listan — en påhittad ändringslogg är samma obefogade säkerhet som produkten finns för att undvika. Första siffran höjs bara när GARANTIERNA ändras, vilket hänt två gånger: 2.0 när fakturan blev ett härlett dokument, 3.0 när TÜV-härdningen ändrade vad loggen garanterar. Nuvarande: 3.1. Och ett fynd på vägen: ritningsstämpeln läste versionen ur den AKTUELLA konstanten, så ett ärende stängt under 1.0 visade 3.1 — precis det stämpelns egen beskrivning säger att den inte ska göra. Avslutshändelsen bär numera versionen, och stämpeln läser den därifrån. Äldre ärenden säger uttryckligen "ej registrerad" i stället för att låna dagens siffra. ---- Tysk kravbild i foten ---------------------------------------------- Listan är tysk därför att § 5 DDG är den strängaste och mest utkrävbara i EU: klarar man den klarar man de andra marknaderna på köpet. Två fällor som är aktuella just nu, och bägge ser fortfarande ut som upplysningar: ODR-LÄNKEN SKA BORT. Förordning 524/2013 upphävdes genom förordning (EU) 2024/3228 och plattformen stängdes 20 juli 2025. Länken har legat i tiotusentals fotnoter sedan 2016 och pekar numera ingenstans. Foten saknar den medvetet. § 5 TMG HETER § 5 DDG sedan 14 maj 2024. Modulen hittar inte på uppgifter. Ett Impressum med påhittad adress är inte ett halvfärdigt Impressum utan ett vilseledande, och skadan större än den tomma rutans. Osatta fält redovisas som en åtgärdslista med rättslig grund, så driften kan spärra en driftsättning på listan i stället för att upptäcka bristen när brevet kommer. 460 enhetstester · genomgången 4/4 · typkontroll ren. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
31727a4ea0 |
Härda enligt revision 2: stäng schemat, ta bort dubbelregeln
C-5. granskaHändelse itererade schemats nycklar och aldrig händelsens, så okända fält accepterades och sparades ordagrant. Två garantier vilade på motsatsen: krypto-shreddingen skyddar en fast lista av fältnamn, så personuppgifter på en vanlig observation krypterades aldrig och överlevde raderingen — och delningsfiltret är typnivå, så samma fält gick ut i kundens delningslänk. Schemat är nu stängt, med varje valfritt fält deklarerat. Det gäller även `kalla`: protokollinläsningen fungerade bara därför att schemat var öppet. Avslaget är hårt, inte en tyst strykning. Verifierat mot riktig trafik — samtliga händelser som klienten faktiskt producerar passerar. M-7. Klienten upprepar inte längre grindens regel utan anropar grinda() och visar dess egna hinder. Villkoret hade glidit isär två gånger utan att något test märkte det. Det avslöjade omedelbart ett verkligt fel i grinden: den krävde textresultat även på kontroller vars krav är foto — trots att gränssnittet märker fältet "Observation (valfritt)". Servern hade alltså nekat avslut på nästan varje riktigt ärende, och det syntes inte så länge klienten hade ett eget och mildare villkor. Evidens graderas nu efter kontrollens eget krav. M-8. Protokollinläsningen svarar med utfall per händelse i stället för en siffra, 207 vid delvis lyckad inläsning, innehållshärledda id:n i stället för klockan, och en transaktion runt hela importen. M-9. En genererad webhookhemlighet lämnas ut en gång vid skapandet. m-8. Profilens vägslagning begränsas till egna egenskaper. m-10. Ett mätvärde som kommer ur en kontroll redovisas en gång. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |
||
|
|
01189ae42a |
ALVA-RULE-200: ett ärende stängs aldrig utan ett varför
Kravet är enkelt att formulera och lätt att bygga fel. En obligatorisk
fritextruta blir "klart" på tredje ärendet, och då har vi bara gjort
dokumentationen långsammare utan att göra den bättre.
Därför fyra frågor med olika adressat i stället för ett fält:
MOTIVERING Varför följer slutsatsen av evidensen? Detta är raden som
saknas i varje verkstadsprotokoll. Underlaget säger vad
som mättes, slutsatsen vad som är fel — ingenting säger
varför det ena medför det andra, och det är precis det
steget en försäkringsbedömare granskar.
UTESLUTET Vad övervägdes och varför föll det bort?
ÅTGÄRDSVAL Varför denna åtgärd och inte en annan?
KVARSTÅENDE Vad är fortfarande osäkert? Får vara "inget" — men aktivt.
Kvalitetsgranskningen är riktad mot hur någon med bråttom faktiskt
skriver: en katalog över icke-svar (klart, åtgärdat, trasig, se ovan,
vet ej), minsta längd, och kravet att texten bär ett orsakssamband eller
refererar konkret evidens. Den som skriver "12,4 V vid stift 14"
hänvisar till en mätning utan att säga ordet — regeln får inte tvinga
fram ett språkbruk som inte är teknikerns.
Den regel som gör underlaget användbart för ett försäkringsbolag härleds
ur loggen: en hypotes som dokumenterats och inte blivit slutsatsen MÅSTE
bemötas. Utan den är en felsökning en gissning som råkade stämma.
Den ärliga vägen finns: orsaken kunde inte fastställas är ett giltigt
utfall, ofta mer användbart än en påhittad orsak — men varför den inte
kunde det är fortfarande ett varför.
Slutsatsen är kunddelbar. Den besvarar "varför kostade det här vad det
kostade" och är den enda rad en bedömare behöver. Att bygga funktionen
och sedan hålla den intern vore att bygga den förgäves.
I gränssnittet granskas fälten medan man skriver, inte efter Spara, och
obemötta hypoteser listas — teknikern ska aldrig behöva gissa vad som
fattas. Det är skillnaden mellan ett krav som respekteras och ett som
kringgås.
---
Typkontrollen avslöjade under arbetet en riktig bugg i mitt eget
grindarbete: schemat och grinden skrevs mot antagna fältnamn i stället
för mot domänmodellen. atgard_utford heter beskrivning och utford, inte
text. Följden var värre än ett typfel — grinden såg utförd åtgärd som
utebliven och krävde därför aldrig kundbesked eller kvalitetskontroll.
Det syntes inte, eftersom testfixturerna hade samma antagande.
Rättat i schema, grind och fixturer, och låst av två nya test: varje
fält i schemat måste finnas i domänmodellen, och varje händelse
demoärendet producerar måste passera serverns validering. Ett schema som
avvisar riktig trafik är värre än inget schema.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt
|
||
|
|
e73decd3f5 |
Revisionen: C-1, C-2, M-2, M-3, m-1 och m-2 åtgärdade
De fyra fynd som underminerade bevisvärdet sitter i samma kodväg och åtgärdas därför tillsammans. C-1 · Härkomsten sätts av servern. anvandare kommer ur den verifierade token och tidpunkt ur serverns klocka. Klientens tidsstämpel kastas inte — vid offline-arbete är den det enda som finns — utan bevaras som registrerad_tidpunkt bredvid mottagningstiden, så glappet blir synligt i stället för osynligt. C-2 · Kvalitetsgrinden flyttad till services/gemensam/grind.mjs och utvärderas nu på servern vid arende_avslutat. Ett avslut som inte passerar får 409 med de faktiska hindren. Metodikdatan flyttades till services/gemensam/metodiker.mjs och klientens metodiker.ts är en typad återexport, så det finns fortfarande exakt en sanning. M-2 · Högvoltsspärren är ett hinder. Ett nekande svar på behörighets- eller spänningsfrihetsfrågan stoppar metodiken i stället för att räknas som besvarad, och spärrar avslutet på servern. Ett obesvarat säkerhetskrav spärrar också — tystnad är inte ett ja. Reglerna ligger i data så nästa farliga metodik bara behöver en rad. M-3 · Händelser valideras mot schema före skrivning. Loggen är append-only, så en felaktig post kan aldrig rättas — kontrollen måste ske innan, efteråt är det för sent för alltid. Okänd typ avvisas. m-1 · Delningskoden använder förkastningsurval i stället för modulo. m-2 · Id-kollisioner räknas i stället för att passera tyst. 21 nya tester låser varje regel. Ett skräddarsytt test kräver dessutom att varje typ i händelseschemat är klassificerad i delningslistan — en ny typ kan alltså varken läcka eller tappas bort. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012EQg3rJsrQ1ZNTvkzmQAtt |