ai-coding-mines목차GitHub

검증기가 잡아야 할 형태를 정상 목록에 넣어뒀다

Python · DB

사고

자동화가 엉뚱한 값을 외부에 써 넣었다. A 거래처의 주문번호가 B 주문의 운송장 번호로 들어갔고, 고객이 배송 추적을 못 하게 됐다.

1차 원인 — 매칭 키가 하나뿐

매칭이 거래처명 하나로만 이뤄졌다. 같은 거래처에 건이 여럿이면 아무 데나 붙는다.

2차 원인 — 이게 진짜다

오류를 잡으라고 만들어둔 탐지 정규식에 ^\d{10,14}$ 를 "정상 형식"으로 등록해 두었다.

그게 바로 이 사고의 형태였다. 10자리 주문번호가 운송장 자리에 들어간 것.

★★ 검증기가 잡아야 할 결함의 서명을 화이트리스트에 올려놓고 있었다.

"이건 정상 형식이니까"라고 넣은 규칙이 정확히 그 사고를 통과시켰다.

★★ 되돌릴 수 없었다

당시에는 "수정 API가 없다" 고 판단했다. 추측한 경로(/invoices/correction, /invoices/update)가 전부 404였기 때문이다.

그 판단이 틀렸다. 수정 경로는 있었고, 이름이 다를 뿐이었다 — 입력은 .../invoices, 수정은 .../updateInvoices. 같은 명사 아래 동사가 붙을 거라 가정했는데 별개 동사 경로였다. 검색 한 번에 나왔고 전건 복구했다.

★★ 추측한 URL의 404는 "기능이 없다"가 아니라 "경로를 모른다"이다. → 아래 별도 항목 참조

⚠️ 복구가 가능했다는 사실이 사고를 가볍게 만들지는 않는다. 잘못된 값은 실제로 고객에게 보였고, 복구는 그걸 되돌린 게 아니라 덮은 것이다.

비가역 쓰기는 사전 검증 말고는 방어 수단이 없다. 사후 교정 경로가 있을 거라 가정하고 설계하면 안 된다.

규율

  1. 검증기를 짤 때, 그 검증기가 잡아야 할 결함의 형태가 정상 목록에 있는지 본다
  2. 탐지 규칙은 과거 사고 사례로 역검증한다. 실제로 났던 오류를 넣어보고 걸리는지 확인한다. 안 걸리면 그 규칙은 없는 것과 같다
  3. 외부에 쓰는 값은 형식 검증 + 대상 매칭을 둘 다 건다. 하나만으로는 부족하다

곁가지 — 도달하지 않아 무사했던 결함

이 결함은 몇 주 전에 이미 기록돼 있었다. 그때는 쓰기가 실제로 상대 시스템에 도달하지 않아서 피해가 없었고, 그래서 "확인됨" 정도로 남았다.

★★ 도달하지 않아서 무사했던 결함은 고쳐진 것이 아니다. 경로가 열리는 날 그대로 터진다.

최초 사고의 원인 넷 — 전부 "형태가 그럴듯한" 값이 통과한 것이다

원인처방
송장 정규식 (\d{10,14})주문번호를 송장으로 오인순수 숫자 패턴을 버리고 캐리어 접두 패턴만 채택
"getting your order ready to be shipped"shipped 부분일치를 발송으로 판정"준비중" 패턴을 따로 두고 주문접수/발송 분리
keyword 빈 값이 필터를 전부 통과해 첫 주문에 무조건 기입keyword 없으면 중단
같은 거래처 주문이 2건 이상이면 첫 건에 기입모호하면 AMBIG 반환, 자동입력 중단

처방이 노트에 적힌 것과 모든 호출 지점에 들어간 것은 별개다. 재발 때 열어 보니 넷 중 ①과 ④가 그 경로에 없었다. 고친 함수가 아니라 그 값을 쓰는 곳 전부를 센다.

거울상 — 정상 동작을 위반 지표로 넣으면 오탐 생성기가 된다

위는 잡아야 할 결함을 정상 목록에 넣은 것이고, 이건 반대다. 표 셋의 정렬을 검사하는 도구에 "글자 시작 x 산포"를 위반 지표로 넣었더니 세 표 전부 "정렬 깨짐"으로 나왔다. 우정렬 금액 열은 오른쪽 끝이 모든 행에서 같은 값이었고, 시작점이 갈린 건 자릿수 차이 그 자체였다. 중앙정렬 열은 뱃지 폭이 다르면 양끝이 갈리는 게 중앙정렬의 정의다. 수리 후 재측정에서 값이 하나도 안 변한 것이 반증이었다 — 진범은 다른 열에 있었다.

정렬맞아야 하는 것갈려도 되는 것
좌정렬시작(left)
우정렬끝(right)시작
중앙중심양끝

지표를 고르기 전에 "이 숫자가 크면 뭐가 잘못된 건가"를 먼저 답한다. 답이 없으면 그 숫자는 근거처럼 보이는 오독을 만든다 — 숫자가 붙어 있어서 근거 없는 추측보다 나쁘다.