class JstlExpressionWhiteLister extends BasicInjectionDetector implements TaintFrameAdditionalVisitor {

    private static final String SYSTEM_PROPERTY = "findsecbugs.jstlsafe.customregexfile";

    private static final Pattern TAG_FOR_HTML_CONTENT_PATTERN = Pattern.compile("\\$\\{e:forHtmlContent\\([a-zA-Z0-9\\._]+\\)\\}");
    private static final Pattern TAG_TO_SAFE_JSON_PATTERN = Pattern.compile("^\\$\\{\\s*[a-zA-Z]+:toSafeJSON\\([a-zA-Z0-9\\-#,\\\'\\\"\\&\\[\\]@\\\\ \\._\\(\\):]+\\)\\s*\\}$");
    private static final Pattern TAG_SAFE_QUOTE_PATTERN = Pattern.compile("^\\$\\{\\s*[a-zA-Z]+:safeQuote\\([a-zA-Z0-9\\-#,\\\'\\\"\\&\\[\\]@\\\\ \\._\\(\\):]+\\)\\s*\\}$");
    private static final String CONTEXT_PATH_PATTERN = "${pageContext.request.contextPath}";

    private static final InvokeMatcherBuilder PROPRIETARY_EVALUATE = invokeInstruction()
            .atClass("org/apache/jasper/runtime/PageContextImpl")
            .atMethod("proprietaryEvaluate")
            .withArgs("(Ljava/lang/String;Ljava/lang/Class;Ljavax/servlet/jsp/PageContext;Lorg/apache/jasper/runtime/ProtectedFunctionMapper;)Ljava/lang/Object;");

    private final List<Pattern> safePatterns;

    public JstlExpressionWhiteLister(BugReporter bugReporter) {
        super(bugReporter);

        this.safePatterns = getCustomPatterns();

        registerVisitor(this);
    }


    @Override
    public void visitInvoke(InvokeInstruction invoke, MethodGen methodGen, TaintFrame frameType, List<Taint> parameters, ConstantPoolGen cpg) throws DataflowAnalysisException {

        if(PROPRIETARY_EVALUATE.matches(invoke,cpg)) {
            Taint defaultVal = parameters.get(3); //The expression is the fourth parameter starting from the right. (Top of the stack last arguments)
            if(defaultVal.getConstantValue() != null) {

                String expression = defaultVal.getConstantValue();
                if(TAG_FOR_HTML_CONTENT_PATTERN.matcher(expression).find() ||
                   TAG_TO_SAFE_JSON_PATTERN.matcher(expression).find() ||
                   TAG_SAFE_QUOTE_PATTERN.matcher(expression).find() ||
                   CONTEXT_PATH_PATTERN.equals(expression)) {

                    Taint value = frameType.getTopValue();
                    value.addTag(Taint.Tag.XSS_SAFE);
                }
                else {
                    for (final Pattern safePattern : safePatterns) {
                        if (safePattern.matcher(expression).find()) {
                            Taint value = frameType.getTopValue();
                            value.addTag(Taint.Tag.XSS_SAFE);
                        }
                    }
                }
            }
        }
    }

    @Override
    public void visitLoad(LoadInstruction load, MethodGen methodGen, TaintFrame frameType, int numProduced, ConstantPoolGen cpg) {
    }

    @Override
    public void visitField(FieldInstruction put, MethodGen methodGen, TaintFrame frameType, Taint taintFrame, int numProduced, ConstantPoolGen cpg) throws Exception {
    }

    @Override
    public void visitReturn(MethodGen methodGen, Taint returnValue, ConstantPoolGen cpg) throws Exception {
    }

    private static List<Pattern> getCustomPatterns() {
        final List<Pattern> safePatterns = new ArrayList<>();
        String propertyValue = FindSecBugsGlobalConfig.getInstance().loadFromSystem(SYSTEM_PROPERTY, "");
        if(!propertyValue.trim().equals("")) {
            for (final String patternFile : propertyValue.split(File.pathSeparator)) {
                safePatterns.addAll(customPatternsFromFile(patternFile));
            }
        }
        return safePatterns;
    }

    private static List<Pattern> customPatternsFromFile(final String path) {
        File file = new File(path);
        try (InputStream stream = file.exists()
             ? new FileInputStream(file)
             : JstlExpressionWhiteLister.class.getClassLoader().getResourceAsStream(path)) {
            if (stream == null) {
                String message = String.format("Could not add custom patterns. "
                        + "Neither file %s nor resource matching %s found.",
                        file.getAbsolutePath(), path);
                throw new IllegalArgumentException(message);
            }

            return patternsFromStream(stream);
        } catch (IOException ex) {
            throw new RuntimeException("Cannot load custom safe regex patterns from " + path, ex);
        }
    }

    private static List<Pattern> patternsFromStream(final InputStream stream) throws IOException
    {<FILL_FUNCTION_BODY>}
}

        final List<Pattern> patterns = new ArrayList<>();

        try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream, UTF_8))) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (!"".equals(line.trim())) {
                    patterns.add(Pattern.compile(line));
                }
            }
        }
        return patterns;
    
<methods>public void registerVisitor(com.h3xstream.findsecbugs.taintanalysis.TaintFrameAdditionalVisitor) <variables>static com.h3xstream.findsecbugs.injection.ClassMethodSignature OBJECT,private static final com.h3xstream.findsecbugs.injection.SinksLoader SINKS_LOADER,private final Map<com.h3xstream.findsecbugs.injection.ClassMethodSignature,com.h3xstream.findsecbugs.injection.InjectionPoint> injectionMap