Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 34 additions & 11 deletions src/Proven/SafeHTTP/Proofs.idr
Original file line number Diff line number Diff line change
Expand Up @@ -90,20 +90,43 @@ parseEmptyMethodFails = Refl
-- HTTP Status Code Properties
--------------------------------------------------------------------------------

||| Success status codes are not errors.
||| If isSuccess returns True, isError must return False.
||| Proved by contradiction: if code >= 200 && code < 300, then code < 400.
||| OWED: success status codes are not errors. If `isSuccess sc = True`
||| (i.e. `sc.code >= 200 && sc.code < 300`), then `isError sc = False`
||| (i.e. `sc.code >= 400` is false). Held back by Idris2 0.8.0 not
||| reducing the comparison-chain `>=`/`<` on the abstract `Nat`
||| `sc.code` to literal `True`/`False` by `Refl` alone — `sc.code` is
||| existentially bound by `MkStatusCode` and only carries the range
||| witness `So (code >= 100 && code <= 599)`, not a concrete value.
||| Same blocker family as SafeChecksum's `Nat`-mod reductions.
||| Discharge once a `Data.Nat` linear-arithmetic reflective tactic
||| is available, or by deriving via `boolAnd`/`boolNot` case-split on
||| the `So` witness plus `lteTransitive` (200 <= sc.code < 300 < 400).
export
successNotError : (sc : StatusCode) -> isSuccess sc = True -> isError sc = False

||| Error status codes are not successes.
0 successNotError : (sc : StatusCode) -> isSuccess sc = True -> isError sc = False

||| OWED: error status codes are not successes. If `isError sc = True`
||| (i.e. `sc.code >= 400`), then `isSuccess sc = False` (i.e.
||| `sc.code >= 200 && sc.code < 300` is false because `sc.code < 300`
||| contradicts `sc.code >= 400`). Held back by Idris2 0.8.0 not
||| reducing `Nat` order comparisons on the abstract record-projected
||| `sc.code` by `Refl`. Same blocker family as `successNotError`.
||| Discharge once a `Data.Nat` linear-arithmetic reflective tactic
||| is available, or by case-split on `>=`/`<` with `lteTransitive`.
export
errorNotSuccess : (sc : StatusCode) -> isError sc = True -> isSuccess sc = False

||| Retryable status codes are errors.
||| 429, 502, 503, 504 are all >= 400.
0 errorNotSuccess : (sc : StatusCode) -> isError sc = True -> isSuccess sc = False

||| OWED: retryable status codes are errors. 429, 502, 503, 504 are
||| each `>= 400`, so `isRetryable sc = True` implies `isError sc =
||| True`. Held back by Idris2 0.8.0 not reducing the four-way `||`
||| equality-disjunction (`sc.code == 429 || ... || sc.code == 504`)
||| into a definite value of `sc.code >= 400` by `Refl` — the `Nat`
||| equality primitives on the abstract record field do not unfold.
||| Same blocker family as `successNotError`. Discharge once a
||| `Data.Nat` reflective tactic is available, or by four-arm case
||| analysis on the `||` chain with literal `lteSucc`-derived proofs
||| `429 >= 400`, `502 >= 400`, `503 >= 400`, `504 >= 400`.
export
retryableIsError : (sc : StatusCode) -> isRetryable sc = True -> isError sc = True
0 retryableIsError : (sc : StatusCode) -> isRetryable sc = True -> isError sc = True

--------------------------------------------------------------------------------
-- Header Injection Prevention Properties
Expand Down
Loading