Kontakta supporten

Vi svarar via e-post, oftast inom två dagar.

Google reCAPTCHA kontrollerar det här inskicket mot missbruk; data skickas till Google. Skriptet laddas först när formuläret öppnas.

← Alla inlägg

Kontrollen som alltid är sann

Det finns 23 satser av typen UPDATE i den här tjänsten. Exakt en av dem kontrollerar om den ändrade något. Den kontrollen är:

'success' => $n->rowCount() >= 0

rowCount() returnerar 0 när ingen rad matchade. Noll är större än eller lika med noll. Kontrollen godkänns vad som än händer.

23 UPDATE-satser, en av dem tittar på resultatet rowCount() >= 0 — godkänns även när inget matchade rowCount() > 0 någonstans i kodbasen: 0 gånger räknare med en rad i tabellen den uppdaterar: 4 av 2 218 inget är trasigt i dag — av ett skäl som inte är den här raden

Vad satsen är till för

Den sparar en kryssruta: om räknarens ägare vill ha en veckosammanfattning via e-post. Adressen ligger i en separat tabell, och en rad hamnar där först när någon har klickat på bekräftelselänken i ett mejl som skickats på begäran. Fyra räknare har en sådan rad. Det finns 2 218 aktiva.

Så för 99,8 % av räknarna matchar UPDATE ingenting. Slutpunkten svarar success: true och inställningen sparas inte, eftersom det inte finns något att spara den i.

Kommentaren ovanför är korrekt

Tre rader högre upp, i samma funktion:

”Bara där en bekräftad returväg finns. Utan en sådan finns det ingen adress som brevet kan gå till — och kryssrutan skulle ha lovat något som inte händer.”

Det är helt rätt. Någon tänkte på det här fallet, förstod det och skrev ner resonemanget. Sedan använde raden under >= där resonemanget krävde >.

Det här är värt att se rakt i ögonen, eftersom den vanliga förklaringen — ingen tänkte på det — ligger nära till hands och är fel. Tänkandet finns i filen. Det som gick fel är ett enda tecken, på ett ställe där fel version och rätt version ser identiska ut vid en snabb blick och beter sig identiskt i varje test som har en rad att uppdatera.

Ingen blir lurad

Här är den del som vore lätt att utelämna, och att utelämna den skulle göra inlägget mer dramatiskt och mindre sant.

Sidan visar inte den kryssrutan om raden inte finns. En skärm bort, i koden som ritar ägarens inställningsruta, frågas samma tabell först, och hela blocket hoppas över när frågan kommer tillbaka tom. En räknarägare utan bekräftad adress ser alltså aldrig kryssrutan, klickar aldrig på den och får aldrig den falska bekräftelsen.

Den trasiga kontrollen kan bara nås genom att anropa slutpunkten direkt med en giltig token. Den som gör det får success: true och ingen sparad inställning. Det är en verklig defekt, och en smal sådan.

Varför det ändå är värt att skriva ner

Funktionen är säker tack vare ett skydd som ingen skrev ner som skyddet. Raden som finns för att göra den säker gör den inte säker. Det som gör det är ett visningsvillkor i en annan funktion, vars kommentar förklarar varför kryssrutan döljs — inte att något är beroende av att den förblir dold.

Ta bort eller strukturera om det visningsvillkoret — en rimlig sak att göra när man gör om en inställningspanel — så blir defekten synlig direkt, utan att något någonstans kopplar ihop de två ändringarna. Säkerheten är verklig, och den är oavsiktlig, och oavsiktlig säkerhet är den sort som försvinner under arbete med något helt annat.

Samma kodbas gör det rätt en fil bort. Webbringens motsvarande funktion frågar om raden finns, returnerar false när den inte gör det och kör aldrig uppdateringen alls. Den tabellen har för närvarande noll rader, så den funktionen returnerar false varje gång den anropas, vilket är det korrekta svaret.

Det allmänna mönstret

En UPDATE som inte matchar några rader är inte ett fel i någon databas. Det är en lyckad sats som inte gjorde något, och varje lager ovanför kommer att rapportera framgång om inte något frågar. Tjugotvå av satserna här frågar inte, och för de flesta av dem är det okej: de uppdaterar en rad som förfrågan redan har bevisat finns.

Den som behövde fråga frågade på ett sätt som inte kan misslyckas. rowCount() >= 0 är ingen svag kontroll, det är avsaknaden av en kontroll, utklädd till en — och den är värre än ingen kontroll alls, eftersom nästa person som läser funktionen ser ett resultat som granskas och slutar leta.

Uttrycket rowCount() > 0 förekommer noll gånger i den här kodbasen. Det är siffran som gjorde ett fel på ett enda tecken till något som är värt ett inlägg: inte att det skrevs fel en gång, utan att det inte fanns någon korrekt förekomst någonstans att jämföra med.

Åtgärdat den 28 augusti 2026. Slutpunkten frågar nu om raden finns innan den uppdaterar, på samma sätt som webbringfunktionen en fil bort redan gjorde, och svarar false när den inte finns. Den uppenbara fixen på ett tecken — att i stället jämföra med större än noll — mättes först och förkastades: drivrutinen rapporterar ändrade rader snarare än matchade rader, så att sätta kryssrutan till det värde den redan har ändrar ingenting, och den versionen skulle ha rapporterat fel för en operation som var i sin ordning. Båda versionerna är fel, åt motsatta håll.

Annons