Coverage Report

Created: 2026-08-31 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mod_auth_openidc/test/fuzz/fuzz_strings.c
Line
Count
Source
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  Licensed under the Apache License,
4
 * Version 2.0 (the "License"); you may not use this file except in
5
 * compliance with the License.  You may obtain a copy of the License at
6
 *
7
 *   http://www.apache.org/licenses/LICENSE-2.0
8
 *
9
 * Copyright (C) 2017-2026 ZmartZone Holding BV - hans.zandbelt@openidc.com
10
 *
11
 * Fuzz target for the small hand-rolled string helpers that run over
12
 * request data before anything has been authenticated: URL encoding and
13
 * decoding (malformed and truncated %-sequences), HTML and JavaScript
14
 * escaping of values echoed into pages, HTTP header name normalization,
15
 * the cookie-domain and hostname-suffix checks, the case-insensitive
16
 * substring search, issuer and space-separated-list comparisons, the query
17
 * string splitter, the pre-verification JOSE header peek (which runs on raw
18
 * tokens from anyone), the state browser fingerprint over the
19
 * X-Forwarded-For and User-Agent headers, the Accept header check, the
20
 * https-URL validator applied to values from responses, the constant-time
21
 * compare, and the log redaction scanners.
22
 *
23
 * None of these is more than a byte loop, which is exactly why they are
24
 * cheap to run in one target: the input is the string, every helper sees
25
 * all of it, and AddressSanitizer catches the over-read that a boundary
26
 * mistake in any of them would turn into.
27
 */
28
29
#include "fuzz.h"
30
/* util.h first: see the include-order note in fuzz_cookie.c */
31
/* clang-format off */
32
#include "util.h"          /* test fixture */
33
#include "cfg/parse.h"     /* oidc_cfg_parse_is_valid_url */
34
#include "http.h"          /* oidc_http_url_encode, ... */
35
#include "http_int.h"      /* oidc_http_redact_body_for_log / _json_for_log */
36
#include "proto/proto.h"   /* oidc_proto_jwt_header_peek */
37
#include "state.h"         /* oidc_state_browser_fingerprint */
38
#include "util/util.h"     /* oidc_util_html_escape, ... */
39
/* clang-format on */
40
41
#include <apr_pools.h>
42
#include <apr_strings.h>
43
#include <apr_tables.h>
44
45
static int g_ready = 0;
46
47
/* engine-called one-time init, pre-forkserver on AFL++: see fuzz.h */
48
34
int LLVMFuzzerInitialize(int *argc, char ***argv) {
49
34
  (void)argc;
50
34
  (void)argv;
51
34
  if (!g_ready) {
52
34
    oidc_test_setup();
53
34
    g_ready = 1;
54
34
  }
55
34
  return 0;
56
34
}
57
58
6.63k
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
59
6.63k
  if (!g_ready)
60
0
    LLVMFuzzerInitialize(NULL, NULL);
61
62
6.63k
  apr_pool_t *pool = NULL;
63
6.63k
  apr_pool_create(&pool, oidc_test_pool_get());
64
65
  /* shallow copy of the fixture request with a per-input pool and a fresh headers_in */
66
6.63k
  request_rec r = *oidc_test_request_get();
67
6.63k
  r.pool = pool;
68
6.63k
  r.headers_in = apr_table_make(pool, 4);
69
70
6.63k
  const char *s = fuzz_strndup(pool, data, size);
71
72
  /* URL encoding: decode, encode, and the round trip */
73
6.63k
  oidc_http_url_encode(&r, oidc_http_url_decode(&r, s));
74
6.63k
  oidc_http_url_decode(&r, oidc_http_url_encode(&r, s));
75
76
  /* escaping of values echoed into pages, and header-name normalization */
77
6.63k
  oidc_util_html_escape(pool, s);
78
6.63k
  oidc_util_html_javascript_escape(pool, s);
79
6.63k
  oidc_http_hdr_normalize_name(&r, s);
80
81
  /* host name checks, both as the host and as the configured domain/suffix */
82
6.63k
  oidc_util_cookie_domain_valid("www.example.com", s);
83
6.63k
  oidc_util_cookie_domain_valid(s, ".example.com");
84
6.63k
  oidc_util_hostname_endswith(s, "example.com");
85
6.63k
  oidc_util_hostname_endswith("www.example.com", s);
86
87
  /* substring search and comparisons */
88
6.63k
  oidc_util_strcasestr(s, "needle");
89
6.63k
  oidc_util_strcasestr("a haystack with a Needle in it", s);
90
6.63k
  oidc_util_issuer_match(s, "https://idp.example.com");
91
6.63k
  oidc_util_issuer_match("https://idp.example.com/", s);
92
6.63k
  oidc_util_spaced_string_equals(pool, s, "code id_token");
93
6.63k
  oidc_util_spaced_string_contains(pool, s, "openid");
94
6.63k
  oidc_util_spaced_string_to_hashtable(pool, s);
95
6.63k
  oidc_util_strcmp_const_time(s, "a-fixed-comparison-value");
96
97
  /* the query string splitter */
98
6.63k
  apr_table_t *params = apr_table_make(pool, 8);
99
6.63k
  oidc_util_table_add_query_encoded_params(pool, params, s);
100
101
  /* the pre-verification JOSE header peek, kid-only and all-fields */
102
6.63k
  char *alg = NULL;
103
6.63k
  char *enc = NULL;
104
6.63k
  char *kid = NULL;
105
6.63k
  oidc_proto_jwt_header_peek(&r, s, NULL, NULL, &kid);
106
6.63k
  oidc_proto_jwt_header_peek(&r, s, &alg, &enc, &kid);
107
108
  /* the state fingerprint over the forwarded-for and user-agent headers, and the Accept check */
109
6.63k
  apr_table_set(r.headers_in, "X-Forwarded-For", s);
110
6.63k
  apr_table_set(r.headers_in, "User-Agent", s);
111
6.63k
  apr_table_set(r.headers_in, "Accept", s);
112
6.63k
  oidc_state_browser_fingerprint(&r, oidc_test_cfg_get(), s);
113
6.63k
  oidc_http_hdr_in_accept_contains(&r, "application/json");
114
115
  /* the https-URL validator run on values from responses */
116
6.63k
  oidc_cfg_parse_is_valid_url(pool, s, "https");
117
6.63k
  oidc_cfg_parse_is_valid_http_url(pool, s);
118
119
  /* the log redaction scanners */
120
6.63k
  oidc_http_param_is_sensitive(s);
121
6.63k
  oidc_http_redact_body_for_log(&r, s);
122
6.63k
  oidc_http_redact_json_for_log(&r, s);
123
124
6.63k
  apr_pool_destroy(pool);
125
6.63k
  return 0;
126
6.63k
}