All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.h3xstream.findsecbugs.xml.ValidatorDetector Maven / Gradle / Ivy

Go to download

Core module of the project. It include all the SpotBugs detectors. The resulting jar is the published plugin.

The newest version!
/**
 * Find Security Bugs
 * Copyright (c) Philippe Arteau, All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3.0 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.
 */
package com.h3xstream.findsecbugs.xml;

import com.h3xstream.findsecbugs.common.ByteCode;
import edu.umd.cs.findbugs.BugInstance;
import edu.umd.cs.findbugs.BugReporter;
import edu.umd.cs.findbugs.Priorities;
import edu.umd.cs.findbugs.ba.AnalysisContext;
import edu.umd.cs.findbugs.ba.CFG;
import edu.umd.cs.findbugs.ba.CFGBuilderException;
import edu.umd.cs.findbugs.ba.ClassContext;
import edu.umd.cs.findbugs.ba.Location;
import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
import org.apache.bcel.Const;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.ICONST;
import org.apache.bcel.generic.INVOKEVIRTUAL;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.InvokeInstruction;
import org.apache.bcel.generic.LDC;

import javax.xml.XMLConstants;

/**
 * Detector for XML External Entity and External Schema processing in javax.xml.validation.Validator
 */
public class ValidatorDetector extends OpcodeStackDetector {

    private static final String XXE_VALIDATOR_TYPE = "XXE_VALIDATOR";

    private static final String VALIDATOR_CLASS_NAME = "javax/xml/validation/Validator";

    private static final String SET_FEATURE_METHOD = "setFeature";

    private static final String SET_PROPERTY_METHOD = "setProperty";

    private static final String VALIDATE_METHOD_NAME = "validate";

    private static final Number BOOLEAN_TRUE_VALUE = 1;

    private static final String EXTERNAL_REFERENCES_DISABLED = "";

    private final BugReporter bugReporter;

    public ValidatorDetector(final BugReporter bugReporter) {
        this.bugReporter = bugReporter;
    }

    @Override
    public void sawOpcode(final int opcode) {
        if (isNotValidateMethod(opcode)) {
            return;
        }

        final ClassContext classContext = getClassContext();
        final CFG cfg;
        try {
            cfg = classContext.getCFG(getMethod());
        } catch (final CFGBuilderException e) {
            AnalysisContext.logError("Cannot get CFG", e);
            return;
        }

        final ConstantPoolGen cpg = classContext.getConstantPoolGen();

        boolean secureProcessingEnabled = false;
        boolean accessExternalDtdDisabled = false;
        boolean accessExternalSchemaDisabled = false;

        for (final Location location : cfg.locations()) {
            if (isSecureProcessingEnabled(location, cpg)) {
                secureProcessingEnabled = true;
            } else if (isAccessPropertyDisabled(location, cpg, XMLConstants.ACCESS_EXTERNAL_DTD)) {
                accessExternalDtdDisabled = true;
            } else if (isAccessPropertyDisabled(location, cpg, XMLConstants.ACCESS_EXTERNAL_SCHEMA)) {
                accessExternalSchemaDisabled = true;
            }
        }

        // Enabling Secure Processing or disabling both Access External DTD and Access External Schema are solutions
        if (secureProcessingEnabled || (accessExternalDtdDisabled && accessExternalSchemaDisabled)) {
            return;
        }

        bugReporter.reportBug(new BugInstance(this, XXE_VALIDATOR_TYPE, Priorities.HIGH_PRIORITY)
                .addClass(this).addMethod(this).addSourceLine(this));
    }

    private boolean isNotValidateMethod(final int opcode) {
        boolean notValidateInvocation = true;

        if (Const.INVOKEVIRTUAL == opcode) {
            final String slashedClassName = getClassConstantOperand();
            final String methodName = getNameConstantOperand();
            if (VALIDATOR_CLASS_NAME.equals(slashedClassName) && VALIDATE_METHOD_NAME.equals(methodName)) {
                notValidateInvocation = false;
            }
        }

        return notValidateInvocation;
    }

    private boolean isSecureProcessingEnabled(final Location location, final ConstantPoolGen cpg) {
        boolean enabled = false;
        final Instruction instruction = location.getHandle().getInstruction();

        if (instruction instanceof INVOKEVIRTUAL) {
            final InvokeInstruction invokeInstruction = (InvokeInstruction) instruction;
            final String instructionMethodName = invokeInstruction.getMethodName(cpg);
            final InstructionHandle handle = location.getHandle();
            if (SET_FEATURE_METHOD.equals(instructionMethodName)) {
                final Object ldcValue = getLdcValue(handle, cpg);
                if (XMLConstants.FEATURE_SECURE_PROCESSING.equals(ldcValue)) {
                    final ICONST constant = ByteCode.getPrevInstruction(handle, ICONST.class);
                    enabled = constant != null && BOOLEAN_TRUE_VALUE.equals(constant.getValue());
                }
            }
        }

        return enabled;
    }

    private boolean isAccessPropertyDisabled(final Location location, final ConstantPoolGen cpg, final String accessPropertyName) {
        boolean enabled = false;
        final Instruction instruction = location.getHandle().getInstruction();

        if (instruction instanceof INVOKEVIRTUAL) {
            final InvokeInstruction invokeInstruction = (InvokeInstruction) instruction;
            final String instructionMethodName = invokeInstruction.getMethodName(cpg);
            final InstructionHandle handle = location.getHandle();
            if (SET_PROPERTY_METHOD.equals(instructionMethodName)) {
                final Object propertyName = getLdcValue(handle.getPrev(), cpg);
                final Object propertyValue = getLdcValue(handle, cpg);
                if (accessPropertyName.equals(propertyName)) {
                    enabled = EXTERNAL_REFERENCES_DISABLED.equals(propertyValue);
                }
            }
        }

        return enabled;
    }

    private Object getLdcValue(final InstructionHandle instructionHandle, final ConstantPoolGen cpg) {
        final LDC ldc = ByteCode.getPrevInstruction(instructionHandle, LDC.class);
        return ldc == null ? null : ldc.getValue(cpg);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy