[Fable 5][P3] PUT /admin/jobs/settings/{job_type} для отсутствующей строки молча теряет изменения и отвечает 200 с fallback-дефолтами #1223

Closed
opened 2026-06-13 04:49:46 +00:00 by bot-backend · 0 comments
Collaborator

Из армейского ревью Claude Fable 5 (2026-06-11)

Confirmed by 2/2 адверсариальных верификатора, severity P3 (api-contract), confidence 0.6.

Файл

backend/app/services/job_settings.py:229

Evidence (цитата кода)

    db.execute(
        text(f"UPDATE job_settings SET {', '.join(sets)} WHERE job_type = :jt"),
        params,
    )
    db.commit()
    return get_one(job_type, db)

Why bug

UPDATE по job_type без строки в job_settings затрагивает 0 строк, rowcount не проверяется; get_one затем возвращает hardcoded _fallback. PUT отвечает 200, админ считает настройку сохранённой, но она потеряна. Сценарий реален: сервис сам держит fallback-ветку для «строка не найдена» (новый job_type до seed-миграции, опечатка в path) — в этих же случаях PUT тихо no-op.

Suggested fix

Проверять result.rowcount: при 0 — 404 (валидировать job_type по известному набору) либо UPSERT (INSERT ... ON CONFLICT (job_type) DO UPDATE), чтобы изменения не терялись.

Verify notes

  • [real/P3] Трасса: PUT /admin/jobs/settings/{job_type} — роут принимает голый str без валидации. Для отсутствующей строки UPDATE матчит 0 строк, rowcount не проверен, commit; get_one → row is None → _fallback с hardcoded-дефолтами; dict проходит JobSettingRead → HTTP 200, изменение молча потеряно. Кейс реален: сервис сам документирует «строка отсутствует» (до миграции 81, опечатка). P3: admin-only, на проде строки засеяны, mismatch виден в теле ответа.
  • [real/P3] Подтверждено: UPDATE без проверки rowcount (стр. 229), затем get_one → _fallback при row is None; эндпоинт принимает произвольный str без 404/enum (admin_jobs.py:46). PUT по отсутствующей строке отдаёт 200 с дефолтами, фронт тостит «сохранено». Защита (seed 81_job_settings.sql + UI шлёт типы из GET) покрывает прод для 4 типов — потому P3. Но дефект достижим: curl-опечатка, пустая таблица в dev (GET отдаёт fallback-список); 16 других v1-эндпоинтов следуют 404-конвенции.

Provenance

  • Finders: schemas-models
  • Confidence: 0.6
  • Originally batched at: main@d796027
## Из армейского ревью Claude Fable 5 (2026-06-11) Confirmed by 2/2 адверсариальных верификатора, severity **P3** (api-contract), confidence 0.6. ## Файл [`backend/app/services/job_settings.py:229`](backend/app/services/job_settings.py:229) ## Evidence (цитата кода) ``` db.execute( text(f"UPDATE job_settings SET {', '.join(sets)} WHERE job_type = :jt"), params, ) db.commit() return get_one(job_type, db) ``` ## Why bug UPDATE по job_type без строки в job_settings затрагивает 0 строк, rowcount не проверяется; get_one затем возвращает hardcoded _fallback. PUT отвечает 200, админ считает настройку сохранённой, но она потеряна. Сценарий реален: сервис сам держит fallback-ветку для «строка не найдена» (новый job_type до seed-миграции, опечатка в path) — в этих же случаях PUT тихо no-op. ## Suggested fix Проверять result.rowcount: при 0 — 404 (валидировать job_type по известному набору) либо UPSERT (INSERT ... ON CONFLICT (job_type) DO UPDATE), чтобы изменения не терялись. ## Verify notes - [real/P3] Трасса: PUT /admin/jobs/settings/{job_type} — роут принимает голый str без валидации. Для отсутствующей строки UPDATE матчит 0 строк, rowcount не проверен, commit; get_one → row is None → _fallback с hardcoded-дефолтами; dict проходит JobSettingRead → HTTP 200, изменение молча потеряно. Кейс реален: сервис сам документирует «строка отсутствует» (до миграции 81, опечатка). P3: admin-only, на проде строки засеяны, mismatch виден в теле ответа. - [real/P3] Подтверждено: UPDATE без проверки rowcount (стр. 229), затем get_one → _fallback при row is None; эндпоинт принимает произвольный str без 404/enum (admin_jobs.py:46). PUT по отсутствующей строке отдаёт 200 с дефолтами, фронт тостит «сохранено». Защита (seed 81_job_settings.sql + UI шлёт типы из GET) покрывает прод для 4 типов — потому P3. Но дефект достижим: curl-опечатка, пустая таблица в dev (GET отдаёт fallback-список); 16 других v1-эндпоинтов следуют 404-конвенции. ## Provenance - Finders: `schemas-models` - Confidence: 0.6 - Originally batched at: main@d796027
bot-backend added the
Fable 5 ревью
label 2026-06-13 04:49:46 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1223
No description provided.