class TransformerFactoryDetector extends OpcodeStackDetector {

    private static final String XXE_DTD_TRANSFORM_FACTORY_TYPE = "XXE_DTD_TRANSFORM_FACTORY";
    private static final String XXE_XSLT_TRANSFORM_FACTORY_TYPE = "XXE_XSLT_TRANSFORM_FACTORY";

    private static final String PROPERTY_SUPPORT_DTD = "http://javax.xml.XMLConstants/property/accessExternalDTD";
    private static final String PROPERTY_SUPPORT_STYLESHEET = "http://javax.xml.XMLConstants/property/accessExternalStylesheet";
    private static final String PROPERTY_SECURE_PROCESSING = "http://javax.xml.XMLConstants/feature/secure-processing";

    private final BugReporter bugReporter;

    public TransformerFactoryDetector(BugReporter bugReporter) {
        this.bugReporter = bugReporter;
    }

    @Override
    public void sawOpcode(int seen) {<FILL_FUNCTION_BODY>}
}

        if (seen != Const.INVOKEVIRTUAL && seen != Const.INVOKEINTERFACE && seen != Const.INVOKESTATIC) {
            return;
        }
        String fullClassName = getClassConstantOperand();
        String method = getNameConstantOperand();
        //The method call is doing XML parsing (see class javadoc)
        if (seen == Const.INVOKESTATIC &&
                (fullClassName.equals("javax/xml/transform/TransformerFactory") ||
                fullClassName.equals("javax/xml/transform/sax/SAXTransformerFactory"))
                && method.equals("newInstance")) {
            ClassContext classCtx = getClassContext();
            ConstantPoolGen cpg = classCtx.getConstantPoolGen();
            CFG cfg;
            try {
                cfg = classCtx.getCFG(getMethod());
            } catch (CFGBuilderException e) {
                AnalysisContext.logError("Cannot get CFG", e);
                return;
            }

            //The combination of the 2 following is consider safe
            boolean hasFeatureDTD = false;
            boolean hasFeatureStylesheet = false;

            boolean hasSecureProcessing = false;

            for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
                Location location = i.next();
                Instruction inst = location.getHandle().getInstruction();

                //DTD and Stylesheet disallow
                //factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
                //factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
                if(inst instanceof INVOKEVIRTUAL || inst instanceof INVOKEINTERFACE) {
                    InvokeInstruction invoke = (InvokeInstruction) inst;
                    if ("setAttribute".equals(invoke.getMethodName(cpg))) {
                        LDC propertyConst = ByteCode.getPrevInstruction(location.getHandle().getPrev(), LDC.class);
                        LDC loadConst = ByteCode.getPrevInstruction(location.getHandle(), LDC.class);
                        if (propertyConst != null && loadConst != null) {
                            if (PROPERTY_SUPPORT_DTD.equals(propertyConst.getValue(cpg))) {
                                // All values other than "all", "http" and "jar" will disable external DTD processing.
                                // Since other vulnerable values could be added, we do not want to use a blacklist mechanism.
                                hasFeatureDTD = ( "".equals(loadConst.getValue(cpg)) );
                            } else if (PROPERTY_SUPPORT_STYLESHEET.equals(propertyConst.getValue(cpg))){
                                // All values other than "all", "http" and "jar" will disable external DTD processing.
                                // Since other vulnerable values could be added, we do not want to use a blacklist mechanism.
                                hasFeatureStylesheet = ( "".equals(loadConst.getValue(cpg)) );
                            }
                        }
                    } else if ("setFeature".equals(invoke.getMethodName(cpg))) {
                        LDC propertyConst = ByteCode.getPrevInstruction(location.getHandle().getPrev(), LDC.class);
                        ICONST loadConst = ByteCode.getPrevInstruction(location.getHandle(), ICONST.class);
                        if (propertyConst != null && loadConst != null
                                && PROPERTY_SECURE_PROCESSING.equals(propertyConst.getValue(cpg))){
                            // If SecureProcessing is set to true (loadConst == 1), the call is not vulnerable
                            hasSecureProcessing = loadConst.getValue().equals(1);
                        }
                    }
                }
            }

            // Secure Processing includes all the suggested settings
            if (hasSecureProcessing) {
                return;
            }

            String simpleClassName = fullClassName.substring(fullClassName.lastIndexOf('/') + 1);

            //Raise a bug
            if (!hasFeatureDTD) {
                bugReporter.reportBug(new BugInstance(this, XXE_DTD_TRANSFORM_FACTORY_TYPE, Priorities.NORMAL_PRIORITY) //
                        .addClass(this).addMethod(this).addSourceLine(this)
                        .addString(simpleClassName + "." + method + "(...)"));
            }

            if (!hasFeatureStylesheet) {
                bugReporter.reportBug(new BugInstance(this, XXE_XSLT_TRANSFORM_FACTORY_TYPE, Priorities.NORMAL_PRIORITY) //
                        .addClass(this).addMethod(this).addSourceLine(this)
                        .addString(simpleClassName + "." + method + "(...)"));
            }
        }
    
<no_super_class>