| 1 | package apperr |
| 2 | |
| 3 | import "testing" |
| 4 | |
| 5 | func TestSanitize(t *testing.T) { |
| 6 | cases := map[string]string{ |
| 7 | "user paul@tito.io not found": "user <email> not found", |
| 8 | "Authorization: Bearer abc.def-ghi": "Authorization: Bearer <redacted>", |
| 9 | "basic dXNlcjpwYXNz failed": "basic <redacted> failed", |
| 10 | "GET /x?token=abc123&y=1": "GET /x?token=<redacted>&y=1", |
| 11 | `{"password": "hunter2", "n": 3}`: `{"password": "<redacted>", "n": 3}`, |
| 12 | "api_key=sk-live-1234 ok": "api_key=<redacted> ok", |
| 13 | "digest 0123456789abcdef0123456789abcdef done": "digest <hex> done", |
| 14 | "b64 QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo0MTIzNA== ok": "b64 <hex> ok", |
| 15 | "short 4123 stays": "short 4123 stays", |
| 16 | "nothing here": "nothing here", |
| 17 | // Regression: a long run of letters/digits/"/"/"-"/"_" is not |
| 18 | // automatically a secret — a path or a long identifier must |
| 19 | // survive; only something hex- or base64-shaped is redacted. |
| 20 | "at Object.<anonymous> (/app/node_modules/express/lib/router/index.js:284:7)": "at Object.<anonymous> (/app/node_modules/express/lib/router/index.js:284:7)", |
| 21 | "internalServerErrorHandlerMiddlewareFactory": "internalServerErrorHandlerMiddlewareFactory", |
| 22 | "sess 0123456789abcdef0123456789abcdef0123456789abcdef done": "sess <hex> done", |
| 23 | // A JWT's dot-separated base64url segments: the "." breaks the |
| 24 | // candidate run into two tokens. The first (36 chars, mixes |
| 25 | // digit/upper/lower) clears the 32-char floor and is redacted; |
| 26 | // the second (19 chars) is below the floor and survives — see |
| 27 | // the report for why that is an accepted partial redaction here. |
| 28 | "jwt eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0In0 x": "jwt <hex>.eyJzdWIiOiIxMjM0In0 x", |
| 29 | } |
| 30 | for in, want := range cases { |
| 31 | if got := Sanitize(in); got != want { |
| 32 | t.Errorf("Sanitize(%q) = %q, want %q", in, got, want) |
| 33 | } |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | func TestSanitizePath(t *testing.T) { |
| 38 | cases := map[string]string{ |
| 39 | "/reset/8f3a9c1e2b4d6f708a9b0c1d2e3f4a5b?x=1#frag": "/reset/:token", |
| 40 | "/users/paul@tito.io/edit": "/users/:token/edit", |
| 41 | "/items/550e8400-e29b-41d4-a716-446655440000": "/items/:token", |
| 42 | "/s/QUJDREVGR0hJSktMTU5PUFFSU1RV": "/s/:token", |
| 43 | "/blog/2024-review": "/blog/2024-review", |
| 44 | "/api/v2/orders/42": "/api/v2/orders/42", |
| 45 | "/": "/", |
| 46 | "": "/", |
| 47 | } |
| 48 | for in, want := range cases { |
| 49 | if got := SanitizePath(in); got != want { |
| 50 | t.Errorf("SanitizePath(%q) = %q, want %q", in, got, want) |
| 51 | } |
| 52 | } |
| 53 | } |
| 54 | |