SamlParserFuzzer.java

// Copyright 2023 the cncf-fuzzing authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
///////////////////////////////////////////////////////////////////////////
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import org.keycloak.adapters.saml.config.parsers.HttpClientParser;
import org.keycloak.adapters.saml.config.parsers.IdpParser;
import org.keycloak.adapters.saml.config.parsers.KeyParser;
import org.keycloak.adapters.saml.config.parsers.KeyStoreParser;
import org.keycloak.adapters.saml.config.parsers.KeycloakSamlAdapterParser;
import org.keycloak.adapters.saml.config.parsers.KeycloakSamlAdapterV1Parser;
import org.keycloak.adapters.saml.config.parsers.KeysParser;
import org.keycloak.adapters.saml.config.parsers.PrincipalNameMappingParser;
import org.keycloak.adapters.saml.config.parsers.RoleMappingParser;
import org.keycloak.adapters.saml.config.parsers.RoleMappingsProviderParser;
import org.keycloak.adapters.saml.config.parsers.SingleLogoutServiceParser;
import org.keycloak.adapters.saml.config.parsers.SingleSignOnServiceParser;
import org.keycloak.adapters.saml.config.parsers.SpParser;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.parsers.AnyDomParser;
import org.keycloak.saml.common.parsers.StaxParser;
import org.keycloak.saml.processing.core.parsers.saml.SAML11AssertionParser;
import org.keycloak.saml.processing.core.parsers.saml.SAML11RequestParser;
import org.keycloak.saml.processing.core.parsers.saml.SAML11ResponseParser;
import org.keycloak.saml.processing.core.parsers.saml.SAML11SubjectParser;
import org.keycloak.saml.processing.core.parsers.saml.SAMLParser;
import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAssertionParser;
import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAttributeParser;
import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAttributeStatementParser;
import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAttributeValueParser;
import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAudienceRestrictionParser;
import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAuthnContextParser;
import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLAuthnStatementParser;
import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLConditionsParser;
import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLEncryptedAssertionParser;
import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLProxyRestrictionParser;
import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLSubjectConfirmationDataParser;
import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLSubjectConfirmationParser;
import org.keycloak.saml.processing.core.parsers.saml.assertion.SAMLSubjectParser;
import org.keycloak.saml.processing.core.parsers.saml.mdattr.SAMLEntityAttributesParser;
import org.keycloak.saml.processing.core.parsers.saml.mdui.SAMLUIInfoParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLArtifactResolutionServiceParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLAssertinIDRequestServiceParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLAssertionConsumerServiceParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLAttributeAuthorityDescriptorParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLAttributeConsumingServiceParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLAttributeServiceParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLAuthnAuthorityDescriptorParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLAuthnQueryServiceParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLAuthzServiceParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLContactPersonParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLEncryptionMethodParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLEntityDescriptorParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLExtensionsParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLIDPSSODescriptorParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLKeyDescriptorParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLManageNameIDServiceParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLNameIDMappingServiceParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLOrganizationParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLPDPDescriptorParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLRequestedAttributeParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLSPSSODescriptorParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLSingleLogoutServiceParser;
import org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLSingleSignOnServiceParser;
import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLArtifactResolveParser;
import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLArtifactResponseParser;
import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLAttributeQueryParser;
import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLAuthNRequestParser;
import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLRequestedAuthnContextParser;
import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLResponseParser;
import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLSloRequestParser;
import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLSloResponseParser;
import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLStatusCodeParser;
import org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLStatusParser;
import org.keycloak.saml.processing.core.parsers.saml.xmldsig.DsaKeyValueParser;
import org.keycloak.saml.processing.core.parsers.saml.xmldsig.KeyInfoParser;
import org.keycloak.saml.processing.core.parsers.saml.xmldsig.RsaKeyValueParser;
import org.keycloak.saml.processing.core.parsers.saml.xmldsig.X509DataParser;

/**
 * This fuzzer targets the parse method of all StaxParser implementations. It creates a
 * XMLEventReader with random bytes in UTF-8 encoding and pass it as a source for the a random SAML
 * parser to parse it.
 */
public class SamlParserFuzzer {
  public static void fuzzerTestOneInput(FuzzedDataProvider data) {
    try {
      // Initialise StaxParser object
      StaxParser parser = null;

      // Retrieve or create a random SAML Parser object
      // instance and run the parse method with the
      // random data provided by the XMLEventReader
      // object created above
      switch (data.consumeInt(1, 73)) {
        case 1:
          parser = HttpClientParser.getInstance();
          break;
        case 2:
          parser = IdpParser.getInstance();
          break;
        case 3:
          parser = KeycloakSamlAdapterParser.getInstance();
          break;
        case 4:
          parser = KeycloakSamlAdapterV1Parser.getInstance();
          break;
        case 5:
          parser = KeysParser.getInstance();
          break;
        case 6:
          parser = KeyParser.getInstance();
          break;
        case 7:
          parser = KeyStoreParser.getInstance();
          break;
        case 8:
          parser = PrincipalNameMappingParser.getInstance();
          break;
        case 9:
          parser = RoleMappingsProviderParser.getInstance();
          break;
        case 10:
          parser = RoleMappingParser.getInstance();
          break;
        case 11:
          parser = SingleLogoutServiceParser.getInstance();
          break;
        case 12:
          parser = SingleSignOnServiceParser.getInstance();
          break;
        case 13:
          parser = SpParser.getInstance();
          break;
        case 14:
          QName qName = new QName(data.consumeString(data.consumeInt(1, 1024)));
          parser = AnyDomParser.getInstance(qName);
          break;
        case 15:
          parser = SAMLParser.getInstance();
          break;
        case 16:
          parser = new SAML11SubjectParser();
          break;
        case 17:
          parser = new SAML11ResponseParser();
          break;
        case 18:
          parser = new SAML11RequestParser();
          break;
        case 19:
          parser = new SAML11AssertionParser();
          break;
        case 20:
          parser = SAMLAssertionParser.getInstance();
          break;
        case 21:
          parser = SAMLAttributeParser.getInstance();
          break;
        case 22:
          parser = SAMLAttributeStatementParser.getInstance();
          break;
        case 23:
          parser = SAMLAttributeValueParser.getInstance();
          break;
        case 24:
          parser = SAMLAudienceRestrictionParser.getInstance();
          break;
        case 25:
          parser = SAMLAuthnContextParser.getInstance();
          break;
        case 26:
          parser = SAMLAuthnStatementParser.getInstance();
          break;
        case 27:
          parser = SAMLConditionsParser.getInstance();
          break;
        case 28:
          parser = SAMLEncryptedAssertionParser.getInstance();
          break;
        case 29:
          parser = SAMLProxyRestrictionParser.getInstance();
          break;
        case 30:
          parser = SAMLSubjectConfirmationDataParser.INSTANCE;
          break;
        case 31:
          parser = SAMLSubjectConfirmationParser.INSTANCE;
          break;
        case 32:
          parser = SAMLSubjectParser.getInstance();
          break;
        case 33:
          parser = SAMLEntityAttributesParser.getInstance();
          break;
        case 34:
          parser = SAMLUIInfoParser.getInstance();
          break;
        case 35:
          parser = SAMLArtifactResolutionServiceParser.getInstance();
          break;
        case 36:
          parser = SAMLAssertinIDRequestServiceParser.getInstance();
          break;
        case 37:
          parser = SAMLAssertionConsumerServiceParser.getInstance();
          break;
        case 38:
          parser = SAMLAttributeAuthorityDescriptorParser.getInstance();
          break;
        case 39:
          parser = SAMLAttributeConsumingServiceParser.getInstance();
          break;
        case 40:
          parser = org.keycloak.saml.processing.core.parsers.saml.metadata.SAMLAttributeParser.getInstance();
          break;
        case 41:
          parser = SAMLAttributeServiceParser.getInstance();
          break;
        case 42:
          parser = SAMLAuthnAuthorityDescriptorParser.getInstance();
          break;
        case 43:
          parser = SAMLAuthnQueryServiceParser.getInstance();
          break;
        case 44:
          parser = SAMLAuthzServiceParser.getInstance();
          break;
        case 45:
          parser = SAMLContactPersonParser.getInstance();
          break;
        case 46:
          parser = SAMLEncryptionMethodParser.getInstance();
          break;
        case 47:
          parser = SAMLEntityDescriptorParser.getInstance();
          break;
        case 48:
          parser = SAMLExtensionsParser.getInstance();
          break;
        case 49:
          parser = SAMLIDPSSODescriptorParser.getInstance();
          break;
        case 50:
          parser = SAMLKeyDescriptorParser.getInstance();
          break;
        case 51:
          parser = SAMLManageNameIDServiceParser.getInstance();
          break;
        case 52:
          parser = SAMLNameIDMappingServiceParser.getInstance();
          break;
        case 53:
          parser = SAMLOrganizationParser.getInstance();
          break;
        case 54:
          parser = SAMLPDPDescriptorParser.getInstance();
          break;
        case 55:
          parser = SAMLRequestedAttributeParser.getInstance();
          break;
        case 56:
          parser = SAMLSingleLogoutServiceParser.getInstance();
          break;
        case 57:
          parser = SAMLSingleSignOnServiceParser.getInstance();
          break;
        case 58:
          parser = SAMLSPSSODescriptorParser.getInstance();
          break;
        case 59:
          parser = SAMLArtifactResolveParser.getInstance();
          break;
        case 60:
          parser = SAMLArtifactResponseParser.getInstance();
          break;
        case 61:
          parser = SAMLAttributeQueryParser.getInstance();
          break;
        case 62:
          parser = SAMLAuthNRequestParser.getInstance();
          break;
        case 63:
          parser = org.keycloak.saml.processing.core.parsers.saml.protocol.SAMLExtensionsParser.getInstance();
          break;
        case 64:
          parser = SAMLRequestedAuthnContextParser.getInstance();
          break;
        case 65:
          parser = SAMLResponseParser.getInstance();
          break;
        case 66:
          parser = SAMLSloRequestParser.getInstance();
          break;
        case 67:
          parser = SAMLSloResponseParser.getInstance();
          break;
        case 68:
          parser = SAMLStatusCodeParser.getInstance();
          break;
        case 69:
          parser = SAMLStatusParser.getInstance();
          break;
        case 70:
          parser = DsaKeyValueParser.getInstance();
          break;
        case 71:
          parser = KeyInfoParser.getInstance();
          break;
        case 72:
          parser = RsaKeyValueParser.getInstance();
          break;
        case 73:
          parser = X509DataParser.getInstance();
          break;
      }

      // Initialize a XMLEventReader with InputStream source pointing
      // to a random byte array in UTF-8 encoding retrieved from the
      // FuzzedDataProvider
      byte[] input = data.consumeRemainingAsString().getBytes(StandardCharsets.UTF_8);
      ByteArrayInputStream bais = new ByteArrayInputStream(input);
      XMLEventReader reader = XMLInputFactory.newInstance().createXMLEventReader(bais);

      parser.parse(reader);
    } catch (ParsingException | XMLStreamException | RuntimeException e) {
      // Known exception
    }
  }
}