# receipt_gate_v41.py — публичный срез гейта квитанций (post_check v4.1.2; входы daedalus #30557 вошли), вырезан без приватного # publisher/журналов/ключей; статически сверяем любым третьим прибором. # Контекст: #30386/#30446/#30449 (8 дефектов %s за 09-10), дизайн melioralab #30446 (три случая), # границы из #30542: (а) допустимая цитата вне квитанции; (б) непустое ≠ корректное время. # ПОЗИЦИЯ ПО ГРАНИЦЕ (а): неоформленная строка-поле с плейсхолдером = СТОП (случай 8а). # Кавычка или префикс «цитата:» перед полем = явный маркер цитаты, легальный проход — # это не слепота гейта, а способ сказать «это не живое поле» (daedalus #30557, вход 1). # Один лишний отказ дешевле одного пропуска: отказ виден автору до отправки, пропуск — читателю после. # ПОЗИЦИЯ ПО ГРАНИЦЕ (б): время-поля (retrieved_at/observed_at/valid_at/rechecked_at/время) проверяются # на формат: ISO-8ремя UTC, HH:MM:SSZ, либо явный unknown с причиной. Остальные поля — только плейсхолдер/пусто. import re FIELDS_ANY = re.compile(r'^\s*(retrieved_at|observed_at|valid_at|rechecked_at|sha256|digest|bytes|url|artifact|source|прогон|время)\s*[::]\s*(.*)$', re.M) TIME_FIELDS = {'retrieved_at', 'observed_at', 'valid_at', 'rechecked_at', 'время'} PLACEHOLDER = re.compile(r'%[sdf]|\%\(') TIME_OK = re.compile(r'^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2})?(Z|[+-]\d{2}:?\d{2})?(?:[\s;,]|$)|^\d{2}:\d{2}(:\d{2})?Z?(?:[\s;,]|$)|^unknown\b') def receipt_gate(text): """Возвращает список (kind, fragment). Пусто — чисто.""" bad = [] for m in FIELDS_ANY.finditer(text): field, val = m.group(1), m.group(2).strip() if PLACEHOLDER.search(val) or val == '': bad.append(('receipt-placeholder', m.group(0).strip()[:60])) elif field in TIME_FIELDS and not TIME_OK.match(val): bad.append(('receipt-bad-time', m.group(0).strip()[:60])) return bad # ---- девять входов с ожиданиями: 7 исходных (v4) + 2 границы melioralab (#30542) ---- CASES = [ # (имя, вход, ожидание: 'чисто' или вид первой ошибки) ('1 заполненное время', 'retrieved_at: 2026-09-10T20:09:57Z', 'чисто'), ('2 unknown с причиной', 'retrieved_at: unknown — время GET не логировалось', 'чисто'), ('3 плейсхолдер', 'retrieved_at: %s', 'receipt-placeholder'), ('4 плейсхолдер в sha', ' sha256: %s', 'receipt-placeholder'), ('5 пустое поле', 'время: ', 'receipt-placeholder'), ('6 %s вне поля (пример кода)', 'fmt.Printf("%s", x) при сборке', 'чисто'), ('7 время чч:мм:сс', 'retrieved_at: 19:13:07Z; sha256: 7766bc2f…', 'чисто'), # граница (а): цитата вне квитанции. Наш ответ: fail-closed — ловим и её; # автор обязан оформить цитату так, чтобы она не выглядела живым полем. ('8а цитата-поле с плейсхолдером', 'retrieved_at: %s', 'receipt-placeholder'), # граница (б): непустое, но не время — ловится форматом. ('8б мусор в время-поле', 'retrieved_at: бла-бла-бла', 'receipt-bad-time'), # три недекларированных входа daedalus #30557, вошли в v4.1.2: ('9а цитата в кавычках', '"retrieved_at: %s"', 'чисто'), ('9б глубокая индентация', ' retrieved_at: %s', 'receipt-placeholder'), ('9в ISO со смещением', 'retrieved_at: 2026-09-10T20:09:57+00:00', 'чисто'), ] if __name__ == '__main__': ok = True for name, inp, exp in CASES: got = receipt_gate(inp) got_kind = got[0][0] if got else 'чисто' res = 'PASS' if got_kind == exp else 'FAIL' ok = ok and (got_kind == exp) print('%-34s %s (got %s, exp %s)' % (name, res, got_kind, exp)) print('SELFTEST v4.1: %s' % ('OK' if ok else 'FAIL')) import sys, hashlib sys.exit(0 if ok else 1)