Skip to content

Commit d57d105

Browse files
Add oss-fuzz integration
Add fuzz targets for SAML response decoding, logout response handling, and auth request building using native Go fuzzing. Include oss-fuzz project configuration (Dockerfile, build.sh, project.yaml) for continuous fuzzing via compile_native_go_fuzzer.
1 parent e8596e7 commit d57d105

7 files changed

Lines changed: 227 additions & 0 deletions

File tree

internal/fuzz/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Internal Fuzzing for gosaml2
2+
3+
This directory contains fuzzing targets for gosaml2 that are used with Go's built-in fuzzing functionality and OSS-Fuzz.
4+
5+
## Running Fuzzers Locally
6+
7+
```bash
8+
go test -fuzz=FuzzDecodeResponse ./internal/fuzz/ -fuzztime=30s
9+
go test -fuzz=FuzzLogoutResponse ./internal/fuzz/ -fuzztime=30s
10+
go test -fuzz=FuzzBuildRequest ./internal/fuzz/ -fuzztime=30s
11+
```
12+
13+
## OSS-Fuzz Integration
14+
15+
These fuzzers use native Go fuzzing (`func Fuzz(f *testing.F)`) and are compiled
16+
by OSS-Fuzz using `compile_native_go_fuzzer`. Configuration files for the integration
17+
can be found in the `oss-fuzz` directory.

internal/fuzz/fuzz_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
// Copyright 2025 Russell Haering et al.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package fuzz
16+
17+
import (
18+
"encoding/base64"
19+
"encoding/binary"
20+
"testing"
21+
22+
saml2 "github.com/russellhaering/gosaml2"
23+
)
24+
25+
func FuzzDecodeResponse(f *testing.F) {
26+
f.Fuzz(func(t *testing.T, data []byte) {
27+
encodedResponse := base64.StdEncoding.EncodeToString(data)
28+
29+
_, err := saml2.DecodeUnverifiedBaseResponse(encodedResponse)
30+
if err != nil {
31+
return
32+
}
33+
34+
sp := &saml2.SAMLServiceProvider{}
35+
_, _ = sp.ValidateEncodedResponse(encodedResponse)
36+
})
37+
}
38+
39+
func FuzzLogoutResponse(f *testing.F) {
40+
f.Fuzz(func(t *testing.T, data []byte) {
41+
encodedResponse := base64.StdEncoding.EncodeToString(data)
42+
43+
_, err := saml2.DecodeUnverifiedLogoutResponse(encodedResponse)
44+
if err != nil {
45+
return
46+
}
47+
48+
sp := &saml2.SAMLServiceProvider{}
49+
_, _ = sp.ValidateEncodedLogoutResponsePOST(encodedResponse)
50+
})
51+
}
52+
53+
func FuzzBuildRequest(f *testing.F) {
54+
f.Fuzz(func(t *testing.T, data []byte) {
55+
if len(data) < 8 {
56+
return
57+
}
58+
59+
idValue := binary.LittleEndian.Uint64(data[:8])
60+
relayState := string(data[8:])
61+
62+
if len(relayState) == 0 {
63+
return
64+
}
65+
66+
sp := &saml2.SAMLServiceProvider{
67+
IdentityProviderSSOURL: "https://idp.example.com/sso",
68+
IdentityProviderIssuer: "https://idp.example.com/",
69+
AssertionConsumerServiceURL: "https://sp.example.com/acs",
70+
AudienceURI: "https://sp.example.com/audience",
71+
SignAuthnRequests: idValue%2 == 0,
72+
ForceAuthn: idValue%3 == 0,
73+
IsPassive: idValue%5 == 0,
74+
}
75+
76+
_, _ = sp.BuildAuthURL(relayState)
77+
_, _ = sp.BuildAuthRequest()
78+
})
79+
}

oss-fuzz/Dockerfile

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
#
16+
################################################################################
17+
18+
FROM gcr.io/oss-fuzz-base/base-builder-go
19+
RUN apt-get update && apt-get install -y make cmake
20+
RUN git clone --depth 1 https://github.com/russellhaering/gosaml2
21+
WORKDIR gosaml2
22+
COPY build.sh $SRC/
23+
COPY *.options $SRC/

oss-fuzz/README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# OSS-Fuzz Integration for gosaml2
2+
3+
This directory contains the configuration files necessary for integrating gosaml2 with Google's [OSS-Fuzz](https://github.com/google/oss-fuzz) continuous fuzzing service.
4+
5+
## Files
6+
7+
- `build.sh`: Build script that compiles the fuzzing targets and creates the seed corpora
8+
- `Dockerfile`: Defines the Docker container used for building the fuzzers
9+
- `project.yaml`: Project configuration for OSS-Fuzz
10+
- `fuzz_decode_response.options`: Fuzzer-specific options for the SAML response decoder
11+
12+
## Fuzzing Targets
13+
14+
The actual fuzzing targets are implemented in the `internal/fuzz` directory:
15+
16+
1. `FuzzDecodeResponse`: Fuzzes SAML response decoding and validation
17+
2. `FuzzLogoutResponse`: Fuzzes SAML logout response decoding
18+
3. `FuzzBuildRequest`: Fuzzes SAML authentication request building
19+
4. `FuzzXMLValidation`: Fuzzes XML validation to catch parsing vulnerabilities
20+
21+
## Testing Locally with Docker
22+
23+
To test the OSS-Fuzz integration locally:
24+
25+
```bash
26+
# Clone OSS-Fuzz
27+
git clone https://github.com/google/oss-fuzz
28+
cd oss-fuzz
29+
30+
# Build the image
31+
python infra/helper.py build_image gosaml2
32+
33+
# Build the fuzzers
34+
python infra/helper.py build_fuzzers gosaml2
35+
36+
# Run the fuzzers
37+
python infra/helper.py run_fuzzer gosaml2 fuzz_decode_response
38+
```
39+
40+
## Adding New Fuzzers
41+
42+
To add a new fuzzer:
43+
44+
1. Add the fuzzer implementation to `internal/fuzz/`
45+
2. Update `build.sh` to compile the new fuzzer and create its seed corpus
46+
3. Create fuzzer options file if needed (e.g., `my_new_fuzzer.options`)

oss-fuzz/build.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/bin/bash -eu
2+
# Copyright 2025 Google LLC
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
################################################################################
17+
18+
cd $SRC/gosaml2
19+
20+
# Build fuzzers
21+
compile_native_go_fuzzer github.com/russellhaering/gosaml2/internal/fuzz FuzzDecodeResponse fuzz_decode_response
22+
compile_native_go_fuzzer github.com/russellhaering/gosaml2/internal/fuzz FuzzLogoutResponse fuzz_logout_response
23+
compile_native_go_fuzzer github.com/russellhaering/gosaml2/internal/fuzz FuzzBuildRequest fuzz_build_request
24+
25+
# Create seed corpus
26+
mkdir -p $OUT/fuzz_decode_response_seed_corpus
27+
# Use existing test data as seed corpus
28+
find ./testdata -name '*.b64' -o -name '*.xml' | while read f; do
29+
cp "$f" $OUT/fuzz_decode_response_seed_corpus/
30+
done
31+
zip -j $OUT/fuzz_decode_response_seed_corpus.zip $OUT/fuzz_decode_response_seed_corpus/*
32+
rm -rf $OUT/fuzz_decode_response_seed_corpus
33+
34+
# Create a minimal seed corpus for the logout response
35+
mkdir -p $OUT/fuzz_logout_response_seed_corpus
36+
# Find logout response files if they exist, otherwise use a subset of the general ones
37+
find ./testdata -name '*logout*' -o -name '*.b64' | head -n 5 | while read f; do
38+
cp "$f" $OUT/fuzz_logout_response_seed_corpus/
39+
done
40+
zip -j $OUT/fuzz_logout_response_seed_corpus.zip $OUT/fuzz_logout_response_seed_corpus/*
41+
rm -rf $OUT/fuzz_logout_response_seed_corpus
42+
43+
# Create a minimal seed corpus for build request
44+
mkdir -p $OUT/fuzz_build_request_seed_corpus
45+
echo "relayState" > $OUT/fuzz_build_request_seed_corpus/relaystate
46+
echo "state123456" > $OUT/fuzz_build_request_seed_corpus/state
47+
zip -j $OUT/fuzz_build_request_seed_corpus.zip $OUT/fuzz_build_request_seed_corpus/*
48+
rm -rf $OUT/fuzz_build_request_seed_corpus
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[libfuzzer]
2+
max_len = 10240

oss-fuzz/project.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
homepage: "https://github.com/russellhaering/gosaml2"
2+
primary_contact: "russell.haering@gmail.com"
3+
language: go
4+
main_repo: "https://github.com/russellhaering/gosaml2"
5+
6+
fuzzing_engines:
7+
- libfuzzer
8+
sanitizers:
9+
- address
10+
11+
architectures:
12+
- x86_64

0 commit comments

Comments
 (0)