diff --git a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/sink/SinkModuleBase.java b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/sink/SinkModuleBase.java index ab9ac727304..fbde0f4c962 100644 --- a/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/sink/SinkModuleBase.java +++ b/dd-java-agent/agent-iast/src/main/java/com/datadog/iast/sink/SinkModuleBase.java @@ -19,10 +19,12 @@ import com.datadog.iast.util.ObjectVisitor; import com.datadog.iast.util.RangeBuilder; import datadog.trace.api.Config; +import datadog.trace.api.Pair; import datadog.trace.api.iast.IastContext; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; import datadog.trace.instrumentation.iastinstrumenter.IastExclusionTrie; +import datadog.trace.instrumentation.iastinstrumenter.SourceMapperImpl; import datadog.trace.util.stacktrace.StackWalker; import java.util.Iterator; import java.util.stream.Stream; @@ -210,6 +212,7 @@ protected Evidence checkInjectionDeeply(final VulnerabilityType type, final Obje } @Nullable + @SuppressWarnings("unused") protected Evidence checkInjectionDeeply( final VulnerabilityType type, final Object value, @@ -218,6 +221,7 @@ protected Evidence checkInjectionDeeply( } @Nullable + @SuppressWarnings("unused") protected Evidence checkInjectionDeeply( final VulnerabilityType type, final Object value, @@ -301,7 +305,20 @@ protected Location buildLocation( } protected final StackTraceElement getCurrentStackTrace() { - return stackWalker.walk(SinkModuleBase::findValidPackageForVulnerability); + StackTraceElement stackTraceElement = + stackWalker.walk(SinkModuleBase::findValidPackageForVulnerability); + // If the source mapper is enabled, we should try to map the stack trace element to the original + // source file + if (SourceMapperImpl.INSTANCE != null) { + Pair pair = + SourceMapperImpl.INSTANCE.getFileAndLine( + stackTraceElement.getClassName(), stackTraceElement.getLineNumber()); + if (pair != null && pair.getLeft() != null && pair.getRight() != null) { + return new StackTraceElement( + pair.getLeft(), stackTraceElement.getMethodName(), pair.getLeft(), pair.getRight()); + } + } + return stackTraceElement; } static StackTraceElement findValidPackageForVulnerability( diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/AbstractStratum.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/AbstractStratum.java new file mode 100755 index 00000000000..ea15d4de9cd --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/AbstractStratum.java @@ -0,0 +1,17 @@ +package datadog.trace.agent.tooling.iast.stratum; + +public abstract class AbstractStratum { + private String name; + + public AbstractStratum(final String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } +} diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/EmbeddedStratum.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/EmbeddedStratum.java new file mode 100755 index 00000000000..17801f0b109 --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/EmbeddedStratum.java @@ -0,0 +1,20 @@ +package datadog.trace.agent.tooling.iast.stratum; + +import java.util.ArrayList; +import java.util.List; + +public class EmbeddedStratum extends AbstractStratum { + private final List sourceMapList = new ArrayList<>(); + + public EmbeddedStratum() { + this(""); + } + + public EmbeddedStratum(final String name) { + super(name); + } + + public List getSourceMapList() { + return sourceMapList; + } +} diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/FileInfo.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/FileInfo.java new file mode 100755 index 00000000000..09e63b4bd4f --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/FileInfo.java @@ -0,0 +1,51 @@ +package datadog.trace.agent.tooling.iast.stratum; + +/** + * The fileInfo describes the translated-source file names ... + */ +public class FileInfo { + private int fileId = -1; + + private String inputFileName; + + private String inputFilePath; + + public int getFileId() { + return fileId; + } + + public void setFileId(final int fileId) { + this.fileId = fileId; + } + + public String getInputFileName() { + return inputFileName; + } + + public void setInputFileName(final String inputFileName) { + this.inputFileName = inputFileName; + } + + public String getInputFilePath() { + if (inputFilePath == null) { + return inputFileName; + } + return inputFilePath; + } + + public void setInputFilePath(final String inputFilePath) { + this.inputFilePath = inputFilePath; + } + + @Override + public String toString() { + return "FileInfo [fileId=" + + fileId + + ", inputFileName=" + + inputFileName + + ", inputFilePath=" + + inputFilePath + + "]"; + } +} diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/LineInfo.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/LineInfo.java new file mode 100755 index 00000000000..8e275784f00 --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/LineInfo.java @@ -0,0 +1,103 @@ +package datadog.trace.agent.tooling.iast.stratum; + +/** + * The line section associates line numbers in the output source with line numbers and source names + * in the input source. + * + *

The format of the line section is the line section marker *L on a line by itself, followed by + * the lines of LineInfo. Each LineInfo has the form: + * + *

InputStartLine # LineFileID , RepeatCount : OutputStartLine , OutputLineIncrement where all + * but + * + *

InputStartLine : OutputStartLine are optional. + * + *

... + */ +public class LineInfo { + private int fileId; + + int inputStartLine; + + int repeatCount; + + int outputStartLine; + + int outputLineIncrement; + + private FileInfo fileInfo; + + public LineInfo( + final int fileId, + final int inputStartLine, + final int repeatCount, + final int outputStartLine, + final int outputLineIncrement) { + this.fileId = fileId; + fileInfo = null; + this.inputStartLine = inputStartLine; + this.repeatCount = repeatCount; + this.outputStartLine = outputStartLine; + this.outputLineIncrement = outputLineIncrement; + } + + public LineInfo( + final FileInfo fileInfo, + final int inputStartLine, + final int repeatCount, + final int outputStartLine, + final int outputLineIncrement) { + fileId = -1; + this.fileInfo = fileInfo; + this.inputStartLine = inputStartLine; + this.repeatCount = repeatCount; + this.outputStartLine = outputStartLine; + this.outputLineIncrement = outputLineIncrement; + } + + public int getFileId() { + return fileId; + } + + public int getInputStartLine() { + return inputStartLine; + } + + public int getRepeatCount() { + return repeatCount; + } + + public int getOutputStartLine() { + return outputStartLine; + } + + public int getOutputLineIncrement() { + return outputLineIncrement; + } + + public FileInfo getFileInfo() { + return fileInfo; + } + + public void setFileInfo(final FileInfo fileInfo) { + this.fileInfo = fileInfo; + } + + @Override + public String toString() { + return "LineInfo [fileId=" + + fileId + + ", inputStartLine=" + + inputStartLine + + ", repeatCount=" + + repeatCount + + ", outputStartLine=" + + outputStartLine + + ", outputLineIncrement=" + + outputLineIncrement + + ", fileInfo=" + + fileInfo + + "]\n"; + } +} diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/Location.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/Location.java new file mode 100755 index 00000000000..5789f4f08aa --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/Location.java @@ -0,0 +1,20 @@ +package datadog.trace.agent.tooling.iast.stratum; + +public class Location { + private final FileInfo fileInfo; + + private final int lineNum; + + public Location(final FileInfo fileInfo, final int lineNum) { + this.fileInfo = fileInfo; + this.lineNum = lineNum; + } + + public FileInfo getFileInfo() { + return fileInfo; + } + + public int getLineNum() { + return lineNum; + } +} diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/ParserException.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/ParserException.java new file mode 100755 index 00000000000..f5eb877f9f9 --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/ParserException.java @@ -0,0 +1,12 @@ +package datadog.trace.agent.tooling.iast.stratum; + +public class ParserException extends SourceMapException { + /** */ + private static final long serialVersionUID = 4991227723777615317L; + + public ParserException() {} + + public ParserException(final String msg) { + super(msg); + } +} diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/Resolver.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/Resolver.java new file mode 100755 index 00000000000..ae6cb321437 --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/Resolver.java @@ -0,0 +1,178 @@ +package datadog.trace.agent.tooling.iast.stratum; + +import java.util.Iterator; +import java.util.List; + +public class Resolver { + public SourceMap resolve(final SourceMap sourceMap) { + for (EmbeddedStratum stratum : sourceMap.getEmbeddedStratumList()) { + StratumExt outerStratum = sourceMap.getStratum(stratum.getName()); + if (outerStratum != null) { + for (SourceMap embeddedSourceMap : stratum.getSourceMapList()) { + SourceMap resolvedEmbeddedSourceMap = resolve(embeddedSourceMap); + String outerFileName = resolvedEmbeddedSourceMap.getOutputFileName(); + for (StratumExt embeddedStratum : resolvedEmbeddedSourceMap.getStratumList()) { + StratumExt resolvedStratum = sourceMap.getStratum(embeddedStratum.getName()); + if (resolvedStratum == null) { + resolvedStratum = new StratumExt(embeddedStratum.getName()); + sourceMap.getStratumList().add(resolvedStratum); + } + resolve(new Context(outerStratum, outerFileName, resolvedStratum, embeddedStratum)); + } + } + } + } + sourceMap.getEmbeddedStratumList().clear(); + return sourceMap; + } + + private void resolve(final Context context) { + for (LineInfo eli : context.embeddedStratum.getLineInfo()) { + resolve(context, eli); + } + } + + private void resolve(final Context context, final LineInfo eli) { + Iterator iter; + if (eli.getRepeatCount() > 0) { + for (iter = context.outerStratum.getLineInfo().iterator(); iter.hasNext(); ) { + LineInfo oli = iter.next(); + if (oli.getFileInfo().getInputFileName().equals(context.outerFileName)) { + if (oli.getInputStartLine() <= eli.getOutputStartLine() + && eli.getOutputStartLine() < oli.getInputStartLine() + oli.getRepeatCount()) { + int difference = eli.getOutputStartLine() - oli.getInputStartLine(); + int available = oli.getRepeatCount() - difference; + int completeCount = + Math.min(available / eli.getOutputLineIncrement(), eli.getRepeatCount()); + + FileInfo fileInfo = + getByPath( + context.resolvedStratum.getFileInfo(), eli.getFileInfo().getInputFilePath()); + if (fileInfo == null) { + fileInfo = eli.getFileInfo(); + context.resolvedStratum.getFileInfo().add(fileInfo); + } + if (completeCount > 0) { + LineInfo rli = + new LineInfo( + fileInfo, + eli.getInputStartLine(), + completeCount, + oli.getOutputStartLine() + difference * oli.getOutputLineIncrement(), + eli.getOutputLineIncrement() * oli.getOutputLineIncrement()); + + context.resolvedStratum.addLineInfo(rli); + LineInfo neli = + new LineInfo( + fileInfo, + eli.getInputStartLine() + completeCount, + eli.getRepeatCount() - completeCount, + eli.getOutputStartLine() + completeCount * eli.getOutputLineIncrement(), + eli.getOutputLineIncrement()); + + resolve(context, neli); + } else { + LineInfo rli = + new LineInfo( + fileInfo, + eli.getInputStartLine(), + 1, + oli.getOutputStartLine() + difference * oli.getOutputLineIncrement(), + available); + + context.resolvedStratum.addLineInfo(rli); + LineInfo neli = + new LineInfo( + fileInfo, + eli.getInputStartLine(), + 1, + eli.getOutputStartLine() + available, + eli.getOutputLineIncrement() - available); + + resolve(context, neli); + neli = + new LineInfo( + fileInfo, + eli.getInputStartLine() + 1, + eli.getRepeatCount() - 1, + eli.getOutputStartLine() + eli.getOutputLineIncrement(), + eli.getOutputLineIncrement()); + + resolve(context, neli); + } + } + } + } + } + } + + private FileInfo getByPath(final List list, final String filePath) { + for (FileInfo fileInfo : list) { + if (fileInfo.getInputFilePath().compareTo(filePath) == 0) { + return fileInfo; + } + } + return null; + } + + private class Context { + + StratumExt outerStratum; + + String outerFileName; + + StratumExt resolvedStratum; + + StratumExt embeddedStratum; + + public Context( + final StratumExt outerStratum, + final String outerFileName, + final StratumExt resolvedStratum, + final StratumExt embeddedStratum) { + this.outerStratum = outerStratum; + this.outerFileName = outerFileName; + this.resolvedStratum = resolvedStratum; + this.embeddedStratum = embeddedStratum; + } + } + + public Location resolve(final SourceMap sourceMap, final String stratumName, final int lineNum) { + SourceMap resolvedSourceMap = resolve(sourceMap); + StratumExt stratum = resolvedSourceMap.getStratum(stratumName); + if (stratum == null) { + return new Location(null, lineNum); + } + LineInfo bestFitLineInfo = null; + int bestFitLineNum = lineNum; + int bfOutputStartLine = Integer.MIN_VALUE; + int bfOutputEndLine = Integer.MAX_VALUE; + for (Iterator iter = stratum.getLineInfo().iterator(); iter.hasNext(); ) { + LineInfo lineInfo = iter.next(); + for (int i = 0; i < lineInfo.getRepeatCount(); i++) { + int outputStartLine = lineInfo.getOutputStartLine() + i * lineInfo.getOutputLineIncrement(); + int outputEndLine = + Math.max(outputStartLine, outputStartLine + lineInfo.getOutputLineIncrement() - 1); + if (outputStartLine <= lineNum && lineNum <= outputEndLine) { + if (lineInfo.getOutputLineIncrement() == 1) { + return new Location(lineInfo.getFileInfo(), lineInfo.getInputStartLine() + i); + } + if (bfOutputStartLine <= outputStartLine && outputEndLine <= bfOutputEndLine) { + bestFitLineInfo = lineInfo; + bestFitLineNum = lineInfo.getInputStartLine() + i; + bfOutputStartLine = + bestFitLineInfo.getOutputStartLine() + i * bestFitLineInfo.getOutputLineIncrement(); + bfOutputEndLine = + Math.max( + bfOutputStartLine, + bfOutputStartLine + bestFitLineInfo.getOutputLineIncrement() - 1); + } + } + } + } + if (bestFitLineInfo != null) { + return new Location(bestFitLineInfo.getFileInfo(), bestFitLineNum); + } + return new Location(null, lineNum); + } +} diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/SourceMap.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/SourceMap.java new file mode 100755 index 00000000000..eb3b30b3586 --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/SourceMap.java @@ -0,0 +1,48 @@ +package datadog.trace.agent.tooling.iast.stratum; + +import java.util.ArrayList; +import java.util.List; + +public class SourceMap { + private final String outputFileName; + + private final String defaultStratumName; + + private final List stratumList = new ArrayList<>(); + + private final List embeddedStratumList = new ArrayList<>(); + + public SourceMap(final String outputFileName, final String defaultStratumName) { + this.outputFileName = outputFileName; + this.defaultStratumName = defaultStratumName; + } + + public boolean isResolved() { + return embeddedStratumList.isEmpty(); + } + + public String getOutputFileName() { + return outputFileName; + } + + public String getDefaultStratumName() { + return defaultStratumName; + } + + public List getStratumList() { + return stratumList; + } + + public List getEmbeddedStratumList() { + return embeddedStratumList; + } + + public StratumExt getStratum(final String stratumName) { + for (StratumExt stratum : stratumList) { + if (stratum.getName().compareTo(stratumName) == 0) { + return stratum; + } + } + return null; + } +} diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/SourceMapException.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/SourceMapException.java new file mode 100755 index 00000000000..9c6f2647f34 --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/SourceMapException.java @@ -0,0 +1,12 @@ +package datadog.trace.agent.tooling.iast.stratum; + +public class SourceMapException extends Exception { + /** */ + private static final long serialVersionUID = 254089927846131094L; + + public SourceMapException() {} + + public SourceMapException(final String msg) { + super(msg); + } +} diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/Stratum.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/Stratum.java new file mode 100644 index 00000000000..8342315c0b9 --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/Stratum.java @@ -0,0 +1,16 @@ +package datadog.trace.agent.tooling.iast.stratum; + +import datadog.trace.api.Pair; + +public interface Stratum { + + /** + * Returns the input line number and the input file id for the given output line number. + * + * @param outputLineNumber the class line number + */ + Pair getInputLine(final int outputLineNumber); + + /** Returns the source file for the given file id. */ + String getSourceFile(final int fileId); +} diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/StratumExt.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/StratumExt.java new file mode 100755 index 00000000000..cac2ebdb1d9 --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/StratumExt.java @@ -0,0 +1,98 @@ +package datadog.trace.agent.tooling.iast.stratum; + +import datadog.trace.api.Pair; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class StratumExt extends AbstractStratum implements Stratum { + private final List fileInfo = new ArrayList<>(); + + private int[] lineStart = null; + + private final List lineInfo = new ArrayList<>(); + + private static final Logger LOG = LoggerFactory.getLogger(StratumExt.class); + + public StratumExt() { + this(""); + } + + public StratumExt(final String name) { + super(name); + } + + @Override + public Pair getInputLine(final int outputLineNumber) { + try { + List info = getLineInfo(); + int startPoint = Arrays.binarySearch(getLineStart(), outputLineNumber); + if (startPoint < 0) { + if (startPoint == -1) { + startPoint = 0; + } else { + startPoint = Math.abs(startPoint) - 2; + } + } + int size = info.size(); + for (int i = startPoint; i < size; i++) { + LineInfo li = info.get(i); + final int start = li.outputStartLine; + if (outputLineNumber >= start) { + int offset = li.repeatCount * li.outputLineIncrement - 1; + int stop = li.outputStartLine + offset; + if (outputLineNumber <= stop) { + int rc = (outputLineNumber - li.outputStartLine) / li.outputLineIncrement; + return Pair.of(li.getFileId(), li.inputStartLine + rc); + } + } + } + } catch (Exception e) { + LOG.debug("Could not get input line number from stratum", e); + } + return null; + } + + @Override + public String getSourceFile(final int fileId) { + if (fileInfo.isEmpty()) { + return null; + } + return fileInfo.stream() + .filter(f -> f.getFileId() == fileId) + .findFirst() + .map(FileInfo::getInputFilePath) + .orElse(null); + } + + public List getFileInfo() { + return fileInfo; + } + + public List getLineInfo() { + return lineInfo; + } + + public void addLineInfo(final LineInfo info) { + lineInfo.add(info); + lineInfo.sort(Comparator.comparingInt(LineInfo::getOutputStartLine)); + } + + public int[] getLineStart() { + if (lineStart == null) { + lineStart = new int[lineInfo.size()]; + for (int i = 0; i < lineStart.length; i++) { + lineStart[i] = lineInfo.get(i).getOutputStartLine(); + } + } + return lineStart; + } + + @Override + public String toString() { + return "Stratum [fileInfoList=" + fileInfo + ", lineInfoList=" + lineInfo + "]"; + } +} diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/StratumManager.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/StratumManager.java new file mode 100644 index 00000000000..4318e8a93f4 --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/StratumManager.java @@ -0,0 +1,152 @@ +package datadog.trace.agent.tooling.iast.stratum; + +import datadog.trace.agent.tooling.iast.stratum.parser.Parser; +import datadog.trace.api.Config; +import datadog.trace.api.iast.telemetry.IastMetric; +import datadog.trace.api.iast.telemetry.IastMetricCollector; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import net.bytebuddy.utility.OpenedClassReader; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Manages SMAP information for classes ... + */ +public class StratumManager { + + private static final Logger LOG = LoggerFactory.getLogger(StratumManager.class); + + private final LimitedConcurrentHashMap map; + + public static final StratumManager INSTANCE = + new StratumManager(Config.get().getIastSourceMappingMaxSize()); + + private StratumManager(int sourceMappingLimit) { + // Prevent instantiation + this.map = new LimitedConcurrentHashMap(sourceMappingLimit); + } + + public static boolean shouldBeAnalyzed(final String internalClassName) { + return internalClassName.contains("jsp") + && (internalClassName.contains("_jsp") + || internalClassName.contains("jsp_") + || internalClassName.contains("_tag")); + } + + public void analyzeClass(final byte[] bytes) { + if (map.isLimitReached()) { + return; + } + StratumExt s = getDefaultStratum(bytes); + if (s != null) { + map.put(s.getName(), s); + } + } + + public Stratum get(final String classname) { + return map.get(classname); + } + + private SourceMap getResolvedSmap(final String smap) { + try { + SourceMap[] sourceMaps = new Parser().parse(smap); + + SourceMap result = new Resolver().resolve(sourceMaps[0]); + // clean result object to minimize memory usage + result + .getStratumList() + .forEach(stratum -> stratum.getLineInfo().forEach(li -> li.setFileInfo(null))); + return result; + } catch (Exception e) { + LOG.debug("Could not get resolved source map from smap", e); + } + return null; + } + + private StratumExt getDefaultStratum(final byte[] bytes) { + try { + String[] classData = extractSourceDebugExtensionASM(bytes); + if (classData[1] == null) { + return null; + } + SourceMap smap = getResolvedSmap(classData[1]); + StratumExt stratum = smap != null ? smap.getStratum(smap.getDefaultStratumName()) : null; + + if (stratum == null) { + return null; + } + + stratum.setName(classData[0]); + return stratum; + } catch (Exception e) { + LOG.debug("Could not get default stratum from byte array", e); + } + return null; + } + + /** Get name and debug info */ + private String[] extractSourceDebugExtensionASM(final byte[] classBytes) { + ClassReader cr = new ClassReader(classBytes); + final String[] result = new String[2]; + cr.accept( + new ClassVisitor(OpenedClassReader.ASM_API) { + @Override + public void visit( + final int version, + final int access, + final String name, + final String signature, + final String superName, + final String[] interfaces) { + result[0] = name.replace('/', '.'); + } + + @Override + public void visitSource(final String source, final String debug) { + result[1] = debug; + } + }, + ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES); + + return result; + } + + static class LimitedConcurrentHashMap { + private final int maxSize; + private volatile boolean limitReached = false; + private final Map map = new ConcurrentHashMap<>(); + + public LimitedConcurrentHashMap(int maxSize) { + this.maxSize = maxSize; + } + + public void put(String className, StratumExt value) { + synchronized (this) { + if (limitReached) { + return; + } + map.put(className, value); + if (this.size() >= maxSize) { + IastMetricCollector.add(IastMetric.SOURCE_MAPPING_LIMIT_REACHED, 1); + limitReached = true; + } + } + } + + public int size() { + return map.size(); + } + + public boolean isLimitReached() { + return limitReached; + } + + public StratumExt get(String classname) { + return map.get(classname); + } + } +} diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/parser/Builder.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/parser/Builder.java new file mode 100755 index 00000000000..2a2bc452d2f --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/parser/Builder.java @@ -0,0 +1,18 @@ +package datadog.trace.agent.tooling.iast.stratum.parser; + +import datadog.trace.agent.tooling.iast.stratum.SourceMapException; + +abstract class Builder { + + private final String section; + + Builder(final String section) { + this.section = section; + } + + String getSectionName() { + return section; + } + + abstract void build(State paramState, String[] paramArrayOfString) throws SourceMapException; +} diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/parser/Builders.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/parser/Builders.java new file mode 100644 index 00000000000..82492174db4 --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/parser/Builders.java @@ -0,0 +1,175 @@ +package datadog.trace.agent.tooling.iast.stratum.parser; + +import datadog.trace.agent.tooling.iast.stratum.EmbeddedStratum; +import datadog.trace.agent.tooling.iast.stratum.FileInfo; +import datadog.trace.agent.tooling.iast.stratum.LineInfo; +import datadog.trace.agent.tooling.iast.stratum.SourceMap; +import datadog.trace.agent.tooling.iast.stratum.SourceMapException; +import datadog.trace.agent.tooling.iast.stratum.StratumExt; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A collection of builders to parse SMAP Information. + * https://jakarta.ee/specifications/debugging/2.0/jdsol-spec-2.0#smap-syntax + */ +class Builders { + + private static final String LineInfoPattern = + "(\\d++)(#(\\d++))?(,(\\d++))?:(\\d++)(,(\\d++))?($)"; + + static final Pattern SPLITTER = Pattern.compile(" "); + + public static final Builder closeStratumBuilder() { + return new Builder("C") { + + @Override + public void build(final State state, final String[] lines) throws SourceMapException { + String[] tokens = SPLITTER.split(lines[0], 2); + if (tokens.length < 2 || tokens[1].equals("")) { + throw new SourceMapException("Stratum name expected"); + } + EmbeddedStratum embeddedStratum = new EmbeddedStratum(tokens[1]); + state.pop(embeddedStratum); + } + }; + } + + public static final Builder endSourceMapBuilder() { + return new Builder("E") { + + @Override + public void build(final State state, final String[] lines) throws SourceMapException { + state.endSourceMap(); + } + }; + } + + public static final Builder fileInfoBuilder() { + return new Builder("F") { + + @Override + public void build(final State state, final String[] lines) throws SourceMapException { + if (!state.getStratum().getFileInfo().isEmpty()) { + throw new SourceMapException("Only one file section allowed"); + } + for (int i = 1; i < lines.length; ) { + FileInfo fileInfo = new FileInfo(); + String s = lines[i++]; + String fileId = "0"; + String fileName = ""; + String filePath = ""; + if (s.startsWith("+")) { + String[] tokens = SPLITTER.split(s, 3); + fileId = tokens[1]; + fileName = tokens[2]; + if (i == lines.length) { + throw new SourceMapException("File path expected"); + } + filePath = lines[i++]; + } else { + String[] tokens = SPLITTER.split(s, 2); + fileId = tokens[0]; + fileName = tokens[1]; + filePath = fileName; + } + try { + fileInfo.setFileId(Integer.parseInt(fileId)); + } catch (NumberFormatException nfe) { + throw new SourceMapException("Invalid file id: " + fileId); + } + fileInfo.setInputFileName(fileName); + fileInfo.setInputFilePath(filePath); + state.getStratum().getFileInfo().add(fileInfo); + } + } + }; + } + + public static Builder lineInfoBuilder() { + return new Builder("L") { + + @Override + public void build(final State state, final String[] lines) throws SourceMapException { + if (!state.getStratum().getLineInfo().isEmpty()) { + throw new SourceMapException("Only one line section allowed"); + } + Pattern p = Pattern.compile(LineInfoPattern); + int fileId = 0; + for (int i = 1; i < lines.length; i++) { + int inputStartLine = 1; + int repeatCount = 1; + int outputStartLine = 1; + int outputLineIncrement = 1; + Matcher m = p.matcher(lines[i]); + if (!m.matches()) { + throw new SourceMapException("Invalid line info: " + lines[i]); + } + try { + inputStartLine = Integer.parseInt(m.group(1)); + if (m.group(3) != null) { + fileId = Integer.parseInt(m.group(3)); + } + if (m.group(5) != null) { + repeatCount = Integer.parseInt(m.group(5)); + } + outputStartLine = Integer.parseInt(m.group(6)); + if (m.group(8) != null) { + outputLineIncrement = Integer.parseInt(m.group(8)); + } + } catch (NumberFormatException nfe) { + throw new SourceMapException("Invalid line info: " + lines[i]); + } + LineInfo lineInfo = + new LineInfo( + fileId, inputStartLine, repeatCount, outputStartLine, outputLineIncrement); + state.getStratum().addLineInfo(lineInfo); + } + } + }; + } + + public static Builder sourceMapBuilder() { + return new Builder("SMAP") { + @Override + public void build(final State state, final String[] lines) throws SourceMapException { + if (lines.length < 3) { + throw new SourceMapException("Source map information expected"); + } + SourceMap sourceMap = new SourceMap(lines[1], lines[2]); + state.getParentStratum().getSourceMapList().add(sourceMap); + state.setSourceMap(sourceMap); + } + }; + } + + public static Builder openEmbeddedStratumBuilder() { + return new Builder("O") { + @Override + public void build(final State state, final String[] lines) throws SourceMapException { + String[] tokens = SPLITTER.split(lines[0], 2); + if (tokens.length < 2 || tokens[1].equals("")) { + throw new SourceMapException("Stratum name expected"); + } + EmbeddedStratum embeddedStratum = new EmbeddedStratum(tokens[1]); + state.getSourceMap().getEmbeddedStratumList().add(embeddedStratum); + state.push(embeddedStratum); + } + }; + } + + public static Builder stratumBuilder() { + return new Builder("S") { + @Override + public void build(final State state, final String[] lines) throws SourceMapException { + String[] tokens = SPLITTER.split(lines[0], 2); + if (tokens.length < 2 || tokens[1].equals("")) { + throw new SourceMapException("Stratum name expected"); + } + StratumExt stratum = new StratumExt(tokens[1]); + state.getSourceMap().getStratumList().add(stratum); + state.setStratum(stratum); + } + }; + } +} diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/parser/Parser.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/parser/Parser.java new file mode 100755 index 00000000000..cd3d9d25b33 --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/parser/Parser.java @@ -0,0 +1,151 @@ +package datadog.trace.agent.tooling.iast.stratum.parser; + +import static datadog.trace.agent.tooling.iast.stratum.parser.Builders.SPLITTER; + +import datadog.trace.agent.tooling.iast.stratum.EmbeddedStratum; +import datadog.trace.agent.tooling.iast.stratum.FileInfo; +import datadog.trace.agent.tooling.iast.stratum.LineInfo; +import datadog.trace.agent.tooling.iast.stratum.ParserException; +import datadog.trace.agent.tooling.iast.stratum.SourceMap; +import datadog.trace.agent.tooling.iast.stratum.SourceMapException; +import datadog.trace.agent.tooling.iast.stratum.StratumExt; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class Parser { + private final Map builders = new TreeMap(); + + private final State state = new State(); + + public Parser() { + registerBuilders(); + } + + protected void registerBuilders() { + add(Builders.sourceMapBuilder()); + add(Builders.endSourceMapBuilder()); + add(Builders.stratumBuilder()); + add(Builders.fileInfoBuilder()); + add(Builders.lineInfoBuilder()); + add(Builders.openEmbeddedStratumBuilder()); + add(Builders.closeStratumBuilder()); + } + + private void parseInit() throws SourceMapException { + state.init(); + } + + private SourceMap[] parseDone() throws SourceMapException { + EmbeddedStratum result = state.done(); + resolveLineFileInfo(result); + return result.getSourceMapList().toArray(new SourceMap[0]); + } + + private void resolveLineFileInfo(final EmbeddedStratum embeddedStratum) + throws SourceMapException { + for (Iterator iter = embeddedStratum.getSourceMapList().iterator(); + iter.hasNext(); ) { + SourceMap sourceMap = iter.next(); + resolveLineFileInfo(sourceMap); + } + } + + private void resolveLineFileInfo(final SourceMap sourceMap) throws SourceMapException { + for (Iterator iter = sourceMap.getStratumList().iterator(); iter.hasNext(); ) { + StratumExt stratum = iter.next(); + resolveLineFileInfo(stratum); + } + for (Iterator iter = sourceMap.getEmbeddedStratumList().iterator(); + iter.hasNext(); ) { + EmbeddedStratum stratum = iter.next(); + resolveLineFileInfo(stratum); + } + } + + private void resolveLineFileInfo(final StratumExt stratum) throws SourceMapException { + for (Iterator iter = stratum.getLineInfo().iterator(); iter.hasNext(); ) { + LineInfo lineInfo = iter.next(); + FileInfo fileInfo = get(stratum.getFileInfo(), lineInfo.getFileId()); + if (fileInfo == null) { + throw new ParserException("Invalid file id: " + lineInfo.getFileId()); + } + lineInfo.setFileInfo(fileInfo); + } + } + + public FileInfo get(final List list, final int fileId) { + for (FileInfo fileInfo : list) { + if (fileInfo.getFileId() == fileId) { + return fileInfo; + } + } + return null; + } + + private Builder getBuilder(final String[] lines) throws SourceMapException { + if (lines.length == 0) { + return null; + } + String sectionName = lines[0]; + String[] tokens = SPLITTER.split(lines[0], 2); + if (tokens.length > 1) { + sectionName = tokens[0].trim(); + } + if (sectionName.startsWith("*")) { + sectionName = sectionName.substring("*".length()); + } + return builders.get(sectionName); + } + + private void parseSection(final String[] lines) throws SourceMapException { + Builder builder = getBuilder(lines); + if (builder != null) { + builder.build(state, lines); + } + } + + public SourceMap[] parse(final String source) throws SourceMapException, IOException { + return parse(new StringReader(source)); + } + + public SourceMap[] parse(final Reader reader) throws SourceMapException, IOException { + String line = ""; + try { + parseInit(); + ArrayList lines = new ArrayList(); + BufferedReader br = new BufferedReader(reader); + boolean sectionLine = true; + while ((line = br.readLine()) != null) { + state.lineNumber += 1; + if (line.startsWith("*") || sectionLine && line.equals("SMAP")) { + parseSection(lines.toArray(new String[0])); + lines.clear(); + } + sectionLine = line.startsWith("*"); + lines.add(line); + } + parseSection(lines.toArray(new String[0])); + return parseDone(); + } catch (SourceMapException sme) { + ParserException pe = + new ParserException(sme.getMessage() + ":" + state.lineNumber + ":" + line); + pe.initCause(sme); + throw pe; + } + } + + public void add(final Builder builder) { + builders.put(builder.getSectionName(), builder); + } + + public void remove(final Builder builder) { + builders.remove(builder.getSectionName()); + } +} diff --git a/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/parser/State.java b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/parser/State.java new file mode 100755 index 00000000000..4f6659a07bd --- /dev/null +++ b/dd-java-agent/agent-tooling/src/main/java/datadog/trace/agent/tooling/iast/stratum/parser/State.java @@ -0,0 +1,104 @@ +package datadog.trace.agent.tooling.iast.stratum.parser; + +import datadog.trace.agent.tooling.iast.stratum.EmbeddedStratum; +import datadog.trace.agent.tooling.iast.stratum.ParserException; +import datadog.trace.agent.tooling.iast.stratum.SourceMap; +import datadog.trace.agent.tooling.iast.stratum.SourceMapException; +import datadog.trace.agent.tooling.iast.stratum.StratumExt; +import java.util.ArrayDeque; +import java.util.Deque; + +class State { + private SourceMap sourceMap; + + private StratumExt stratum; + + private EmbeddedStratum parentStratum; + + private final Deque stateStack = new ArrayDeque<>(); + + int lineNumber; + + public void init() { + lineNumber = 0; + sourceMap = null; + stratum = null; + parentStratum = new EmbeddedStratum(); + stateStack.clear(); + } + + public EmbeddedStratum done() throws SourceMapException { + if (!stateStack.isEmpty()) { + throw new ParserException("Unbalanced source map"); + } + return parentStratum; + } + + public SourceMap getSourceMap() { + return sourceMap; + } + + void setSourceMap(final SourceMap sourceMap) throws SourceMapException { + if (this.sourceMap != null) { + throw new ParserException("End of source map expected"); + } + this.sourceMap = sourceMap; + stratum = null; + } + + void endSourceMap() throws SourceMapException { + if (sourceMap == null) { + throw new ParserException("Unexpected end of source map"); + } + sourceMap = null; + stratum = null; + } + + public StratumExt getStratum() throws SourceMapException { + if (stratum == null) { + throw new ParserException("Stratum expected"); + } + return stratum; + } + + void setStratum(final StratumExt stratum) throws SourceMapException { + if (sourceMap == null) { + throw new ParserException("Source map expected"); + } + this.stratum = stratum; + } + + void push(final EmbeddedStratum embeddedStratum) throws SourceMapException { + stateStack.push(new StackItem(sourceMap, parentStratum)); + endSourceMap(); + setParentStratum(embeddedStratum); + } + + void pop(final EmbeddedStratum embeddedStratum) throws SourceMapException { + if (!parentStratum.getName().equals(embeddedStratum.getName())) { + throw new ParserException("Invalid closing embedded stratum: " + embeddedStratum.getName()); + } + StackItem item = stateStack.pop(); + setSourceMap(item.sourceMap); + setParentStratum(item.parentStratum); + } + + public EmbeddedStratum getParentStratum() { + return parentStratum; + } + + private void setParentStratum(final EmbeddedStratum parentStratum) { + this.parentStratum = parentStratum; + } + + private class StackItem { + SourceMap sourceMap; + + EmbeddedStratum parentStratum; + + public StackItem(final SourceMap sourceMap, final EmbeddedStratum parentStratum) { + this.sourceMap = sourceMap; + this.parentStratum = parentStratum; + } + } +} diff --git a/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/tooling/iast/stratum/StratumManagerTest.groovy b/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/tooling/iast/stratum/StratumManagerTest.groovy new file mode 100644 index 00000000000..9fe6c727180 --- /dev/null +++ b/dd-java-agent/agent-tooling/src/test/groovy/datadog/trace/agent/tooling/iast/stratum/StratumManagerTest.groovy @@ -0,0 +1,63 @@ +package datadog.trace.agent.tooling.iast.stratum + +import datadog.trace.test.util.DDSpecification +import org.apache.commons.io.FileUtils + +class StratumManagerTest extends DDSpecification { + + void 'test shouldBeAnalyzed'(){ + + when: + def result = StratumManager.shouldBeAnalyzed(internalClassName) + + then: + result == expected + + where: + internalClassName | expected + 'foo/bar/Baz' | false + 'foo/jsp/Baz' | false + 'foo/bar/Baz_jsp' | true + 'foo/bar/jsp_Baz' | true + 'foo/bar/Baz_tag' | false + 'foo/bar/jsp/Baz_tag' | true + } + + void 'test analyzeClass'(){ + given: + byte[] data = FileUtils.readFileToByteArray(new File("src/test/resources/datadog.trace.agent.tooling.stratum/register_jsp.class")) + + when: + StratumManager.INSTANCE.analyzeClass(data) + + then: + final result = StratumManager.INSTANCE.get("org.apache.jsp.register_jsp") + result != null + final inputLine = result.getInputLine(216) + inputLine.right == 70 + result.getSourceFile(inputLine.left) == "register.jsp" + result + } + + void 'test limit reached'(){ + setup: + def newStratumManager = new StratumManager(1) + byte[] data = FileUtils.readFileToByteArray(new File("src/test/resources/datadog.trace.agent.tooling.stratum/register_jsp.class")) + + when: + newStratumManager.analyzeClass(data) + + then: + final result =newStratumManager.get("org.apache.jsp.register_jsp") + result != null + newStratumManager.map.size() == 1 + newStratumManager.map.isLimitReached() + + when: + newStratumManager.analyzeClass(new byte[0]) + + then: + newStratumManager.map.size() == 1 + newStratumManager.map.isLimitReached() + } +} diff --git a/dd-java-agent/agent-tooling/src/test/resources/datadog.trace.agent.tooling.stratum/register.jsp b/dd-java-agent/agent-tooling/src/test/resources/datadog.trace.agent.tooling.stratum/register.jsp new file mode 100644 index 00000000000..23b41838f7b --- /dev/null +++ b/dd-java-agent/agent-tooling/src/test/resources/datadog.trace.agent.tooling.stratum/register.jsp @@ -0,0 +1,136 @@ +<%@ page import="java.sql.*" %> + +<%@ include file="/dbconnection.jspf" %> + +<% +String username = (String) request.getParameter("username"); +String password1 = (String) request.getParameter("password1"); +String password2 = (String) request.getParameter("password2"); +String usertype = (String) session.getAttribute("usertype"); +String userid = (String) session.getAttribute("userid"); +String debug = ""; +String result = null; +boolean registered = false; + +if (request.getMethod().equals("POST") && username != null) { + if (username == null || username.length() < 5) { + result = "You must supply a username of at least 5 characters."; + + } else if (username.indexOf("@") < 0) { + result = "Invalid username - please supply a valid email address."; + + } else if (password1 == null || password1.length() < 5) { + result = "You must supply a password of at least 5 characters."; + + } else if (password1.equals(password2)) { + Statement stmt = conn.createStatement(); + ResultSet rs = null; + try { + stmt.executeQuery("INSERT INTO Users (name, type, password) VALUES ('" + username + "', 'USER', '" + password1 + "')"); + rs = stmt.executeQuery("SELECT * FROM Users WHERE (name = '" + username + "' AND password = '" + password1 + "')"); + rs.next(); + userid = "" + rs.getInt("userid"); + + session.setAttribute("username", username); + session.setAttribute("usertype", "USER"); + session.setAttribute("userid", userid); + + + if (username.replaceAll("\\s", "").toLowerCase().indexOf("") >= 0) { + conn.createStatement().execute("UPDATE Score SET status = 1 WHERE task = 'XSS_USER'"); + } + + registered = true; + + // Update basket + Cookie[] cookies = request.getCookies(); + String basketId = null; + if (cookies != null) { + for (Cookie cookie : cookies) { + if (cookie.getName().equals("b_id") && cookie.getValue().length() > 0) { + basketId = cookie.getValue(); + break; + } + } + } + if (basketId != null) { + debug += " userId = " + userid + " basketId = " + basketId; + // TODO breaks basket scoring :( + stmt.execute("UPDATE Users SET currentbasketid = " + basketId + " WHERE userid = " + userid); + stmt.execute("UPDATE Baskets SET userid = " + userid + " WHERE basketid = " + basketId); + response.addCookie(new Cookie("b_id", "")); + } + + } catch (SQLException e) { + if (e.getMessage().indexOf("Unique constraint violation") >= 0) { + result = "A user with this name already exists."; + } else { + if ("true".equals(request.getParameter("debug"))) { + conn.createStatement().execute("UPDATE Score SET status = 1 WHERE task = 'HIDDEN_DEBUG'"); + out.println("DEBUG System error: " + e + "

"); + } else { + out.println("System error."); + } + } + } catch (Exception e) { + if ("true".equals(request.getParameter("debug"))) { + conn.createStatement().execute("UPDATE Score SET status = 1 WHERE task = 'HIDDEN_DEBUG'"); + out.println("DEBUG System error: " + e + "

"); + } else { + out.println("System error."); + } + } finally { + stmt.close(); + } + } else { + result = "The passwords you have supplied are different."; + } +} +%> + + +

Register

+<% +if ("true".equals(request.getParameter("debug"))) { + conn.createStatement().execute("UPDATE Score SET status = 1 WHERE task = 'HIDDEN_DEBUG'"); + out.println("DEBUG: " + debug + "

"); +} + +if (registered) { + out.println("
You have successfully registered with The BodgeIt Store."); +%> + +<% + return; + +} else if (result != null) { + out.println("

" + result + "


"); +} +%> + +Please enter the following details to register with us:

+
+
+ + + + + + + + + + + + + + + + + +
Username (your email address):
Password:
Confirm Password:
+
+
+ + + diff --git a/dd-java-agent/agent-tooling/src/test/resources/datadog.trace.agent.tooling.stratum/register_jsp.class b/dd-java-agent/agent-tooling/src/test/resources/datadog.trace.agent.tooling.stratum/register_jsp.class new file mode 100644 index 00000000000..30ef036a592 Binary files /dev/null and b/dd-java-agent/agent-tooling/src/test/resources/datadog.trace.agent.tooling.stratum/register_jsp.class differ diff --git a/dd-java-agent/agent-tooling/src/test/resources/datadog.trace.agent.tooling.stratum/register_jsp.java b/dd-java-agent/agent-tooling/src/test/resources/datadog.trace.agent.tooling.stratum/register_jsp.java new file mode 100644 index 00000000000..90bd4074550 --- /dev/null +++ b/dd-java-agent/agent-tooling/src/test/resources/datadog.trace.agent.tooling.stratum/register_jsp.java @@ -0,0 +1,308 @@ +/* + * Generated by the Jasper component of Apache Tomcat + * Version: Apache Tomcat/8.0.33 + * Generated at: 2019-06-25 17:35:15 UTC + * Note: The last modified time of this file was set to + * the last modified time of the source file after + * generation to assist with modification tracking. + */ +package org.apache.jsp; + +import javax.servlet.*; +import javax.servlet.http.*; +import javax.servlet.jsp.*; +import java.sql.*; +import java.sql.*; + +public final class register_jsp extends org.apache.jasper.runtime.HttpJspBase + implements org.apache.jasper.runtime.JspSourceDependent, + org.apache.jasper.runtime.JspSourceImports { + + + private Connection conn = null; + + public void jspInit() { + try { + // Get hold of the JDBC driver + Class.forName("org.hsqldb.jdbcDriver" ); + // Establish a connection to an in memory db + conn = DriverManager.getConnection("jdbc:hsqldb:mem:SQL", "sa", ""); + } catch (SQLException e) { + getServletContext().log("Db error: " + e); + } catch (Exception e) { + getServletContext().log("System error: " + e); + } + } + + public void jspDestroy() { + try { + if (conn != null) { + conn.close(); + } + } catch (SQLException e) { + getServletContext().log("Db error: " + e); + } catch (Exception e) { + getServletContext().log("System error: " + e); + } + } + + private static final javax.servlet.jsp.JspFactory _jspxFactory = + javax.servlet.jsp.JspFactory.getDefaultFactory(); + + private static java.util.Map _jspx_dependants; + + static { + _jspx_dependants = new java.util.HashMap(1); + _jspx_dependants.put("/dbconnection.jspf", Long.valueOf(1561118130000L)); + } + + private static final java.util.Set _jspx_imports_packages; + + private static final java.util.Set _jspx_imports_classes; + + static { + _jspx_imports_packages = new java.util.HashSet<>(); + _jspx_imports_packages.add("java.sql"); + _jspx_imports_packages.add("javax.servlet"); + _jspx_imports_packages.add("javax.servlet.http"); + _jspx_imports_packages.add("javax.servlet.jsp"); + _jspx_imports_classes = null; + } + + private volatile javax.el.ExpressionFactory _el_expressionfactory; + private volatile org.apache.tomcat.InstanceManager _jsp_instancemanager; + + public java.util.Map getDependants() { + return _jspx_dependants; + } + + public java.util.Set getPackageImports() { + return _jspx_imports_packages; + } + + public java.util.Set getClassImports() { + return _jspx_imports_classes; + } + + public javax.el.ExpressionFactory _jsp_getExpressionFactory() { + if (_el_expressionfactory == null) { + synchronized (this) { + if (_el_expressionfactory == null) { + _el_expressionfactory = _jspxFactory.getJspApplicationContext(getServletConfig().getServletContext()).getExpressionFactory(); + } + } + } + return _el_expressionfactory; + } + + public org.apache.tomcat.InstanceManager _jsp_getInstanceManager() { + if (_jsp_instancemanager == null) { + synchronized (this) { + if (_jsp_instancemanager == null) { + _jsp_instancemanager = org.apache.jasper.runtime.InstanceManagerFactory.getInstanceManager(getServletConfig()); + } + } + } + return _jsp_instancemanager; + } + + public void _jspInit() { + } + + public void _jspDestroy() { + } + + public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response) + throws java.io.IOException, javax.servlet.ServletException { + +final java.lang.String _jspx_method = request.getMethod(); +if (!"GET".equals(_jspx_method) && !"POST".equals(_jspx_method) && !"HEAD".equals(_jspx_method) && !javax.servlet.DispatcherType.ERROR.equals(request.getDispatcherType())) { +response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, "JSPs only permit GET POST or HEAD"); +return; +} + + final javax.servlet.jsp.PageContext pageContext; + javax.servlet.http.HttpSession session = null; + final javax.servlet.ServletContext application; + final javax.servlet.ServletConfig config; + javax.servlet.jsp.JspWriter out = null; + final java.lang.Object page = this; + javax.servlet.jsp.JspWriter _jspx_out = null; + javax.servlet.jsp.PageContext _jspx_page_context = null; + + + try { + response.setContentType("text/html"); + pageContext = _jspxFactory.getPageContext(this, request, response, + null, true, 8192, true); + _jspx_page_context = pageContext; + application = pageContext.getServletContext(); + config = pageContext.getServletConfig(); + session = pageContext.getSession(); + out = pageContext.getOut(); + _jspx_out = out; + + out.write('\n'); + out.write('\n'); + out.write('\r'); + out.write('\n'); + out.write('\n'); + out.write('\n'); + +String username = (String) request.getParameter("username"); +String password1 = (String) request.getParameter("password1"); +String password2 = (String) request.getParameter("password2"); +String usertype = (String) session.getAttribute("usertype"); +String userid = (String) session.getAttribute("userid"); +String debug = ""; +String result = null; +boolean registered = false; + +if (request.getMethod().equals("POST") && username != null) { + if (username == null || username.length() < 5) { + result = "You must supply a username of at least 5 characters."; + + } else if (username.indexOf("@") < 0) { + result = "Invalid username - please supply a valid email address."; + + } else if (password1 == null || password1.length() < 5) { + result = "You must supply a password of at least 5 characters."; + + } else if (password1.equals(password2)) { + Statement stmt = conn.createStatement(); + ResultSet rs = null; + try { + stmt.executeQuery("INSERT INTO Users (name, type, password) VALUES ('" + username + "', 'USER', '" + password1 + "')"); + rs = stmt.executeQuery("SELECT * FROM Users WHERE (name = '" + username + "' AND password = '" + password1 + "')"); + rs.next(); + userid = "" + rs.getInt("userid"); + + session.setAttribute("username", username); + session.setAttribute("usertype", "USER"); + session.setAttribute("userid", userid); + + + if (username.replaceAll("\\s", "").toLowerCase().indexOf("") >= 0) { + conn.createStatement().execute("UPDATE Score SET status = 1 WHERE task = 'XSS_USER'"); + } + + registered = true; + + // Update basket + Cookie[] cookies = request.getCookies(); + String basketId = null; + if (cookies != null) { + for (Cookie cookie : cookies) { + if (cookie.getName().equals("b_id") && cookie.getValue().length() > 0) { + basketId = cookie.getValue(); + break; + } + } + } + if (basketId != null) { + debug += " userId = " + userid + " basketId = " + basketId; + // TODO breaks basket scoring :( + stmt.execute("UPDATE Users SET currentbasketid = " + basketId + " WHERE userid = " + userid); + stmt.execute("UPDATE Baskets SET userid = " + userid + " WHERE basketid = " + basketId); + response.addCookie(new Cookie("b_id", "")); + } + + } catch (SQLException e) { + if (e.getMessage().indexOf("Unique constraint violation") >= 0) { + result = "A user with this name already exists."; + } else { + if ("true".equals(request.getParameter("debug"))) { + conn.createStatement().execute("UPDATE Score SET status = 1 WHERE task = 'HIDDEN_DEBUG'"); + out.println("DEBUG System error: " + e + "

"); + } else { + out.println("System error."); + } + } + } catch (Exception e) { + if ("true".equals(request.getParameter("debug"))) { + conn.createStatement().execute("UPDATE Score SET status = 1 WHERE task = 'HIDDEN_DEBUG'"); + out.println("DEBUG System error: " + e + "

"); + } else { + out.println("System error."); + } + } finally { + stmt.close(); + } + } else { + result = "The passwords you have supplied are different."; + } +} + + out.write('\n'); + out.write('\n'); + org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "/header.jsp", out, false); + out.write("\n"); + out.write("

Register

\n"); + +if ("true".equals(request.getParameter("debug"))) { + conn.createStatement().execute("UPDATE Score SET status = 1 WHERE task = 'HIDDEN_DEBUG'"); + out.println("DEBUG: " + debug + "

"); +} + +if (registered) { + out.println("
You have successfully registered with The BodgeIt Store."); + + out.write('\n'); + out.write(' '); + org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "/footer.jsp", out, false); + out.write('\n'); + + return; + +} else if (result != null) { + out.println("

" + result + "


"); +} + + out.write("\n"); + out.write("\n"); + out.write("Please enter the following details to register with us:

\n"); + out.write("
\n"); + out.write("\t
\n"); + out.write("\t\n"); + out.write("\t\n"); + out.write("\t\t\n"); + out.write("\t\t\n"); + out.write("\t\n"); + out.write("\t\n"); + out.write("\t\t\n"); + out.write("\t\t\n"); + out.write("\t\n"); + out.write("\t\n"); + out.write("\t\t\n"); + out.write("\t\t\n"); + out.write("\t\n"); + out.write("\t\n"); + out.write("\t\t\n"); + out.write("\t\t\n"); + out.write("\t\n"); + out.write("\t
Username (your email address):
Password:
Confirm Password:
\n"); + out.write("\t
\n"); + out.write("
\n"); + out.write("\n"); + org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, "/footer.jsp", out, false); + out.write('\n'); + out.write('\n'); + } catch (java.lang.Throwable t) { + if (!(t instanceof javax.servlet.jsp.SkipPageException)){ + out = _jspx_out; + if (out != null && out.getBufferSize() != 0) + try { + if (response.isCommitted()) { + out.flush(); + } else { + out.clearBuffer(); + } + } catch (java.io.IOException e) {} + if (_jspx_page_context != null) _jspx_page_context.handlePageException(t); + else throw new ServletException(t); + } + } finally { + _jspxFactory.releasePageContext(_jspx_page_context); + } + } +} diff --git a/dd-java-agent/instrumentation/iast-instrumenter/src/main/java/datadog/trace/instrumentation/iastinstrumenter/IastInstrumentation.java b/dd-java-agent/instrumentation/iast-instrumenter/src/main/java/datadog/trace/instrumentation/iastinstrumenter/IastInstrumentation.java index e259dd55796..8ecb8f07e51 100644 --- a/dd-java-agent/instrumentation/iast-instrumenter/src/main/java/datadog/trace/instrumentation/iastinstrumenter/IastInstrumentation.java +++ b/dd-java-agent/instrumentation/iast-instrumenter/src/main/java/datadog/trace/instrumentation/iastinstrumenter/IastInstrumentation.java @@ -46,9 +46,10 @@ protected CallSiteSupplier callSites() { @Override protected Advices buildAdvices(final Iterable callSites) { if (Config.get().isIastHardcodedSecretEnabled()) { - return Advices.fromCallSites(callSites, IastHardcodedSecretListener.INSTANCE); + return Advices.fromCallSites( + callSites, StratumListener.INSTANCE, IastHardcodedSecretListener.INSTANCE); } else { - return Advices.fromCallSites(callSites); + return Advices.fromCallSites(callSites, StratumListener.INSTANCE); } } diff --git a/dd-java-agent/instrumentation/iast-instrumenter/src/main/java/datadog/trace/instrumentation/iastinstrumenter/SourceMapperImpl.java b/dd-java-agent/instrumentation/iast-instrumenter/src/main/java/datadog/trace/instrumentation/iastinstrumenter/SourceMapperImpl.java new file mode 100644 index 00000000000..ef65b6b4ccb --- /dev/null +++ b/dd-java-agent/instrumentation/iast-instrumenter/src/main/java/datadog/trace/instrumentation/iastinstrumenter/SourceMapperImpl.java @@ -0,0 +1,36 @@ +package datadog.trace.instrumentation.iastinstrumenter; + +import datadog.trace.agent.tooling.iast.stratum.Stratum; +import datadog.trace.agent.tooling.iast.stratum.StratumManager; +import datadog.trace.api.Config; +import datadog.trace.api.Pair; +import datadog.trace.api.iast.stratum.SourceMapper; + +public class SourceMapperImpl implements SourceMapper { + + // This is only available if IAST source mapping is enabled + public static final SourceMapperImpl INSTANCE = + Config.get().isIastSourceMappingEnabled() + ? new SourceMapperImpl(StratumManager.INSTANCE) + : null; + + private final StratumManager stratumManager; + + private SourceMapperImpl(StratumManager stratumManager) { + // Prevent instantiation + this.stratumManager = stratumManager; + } + + @Override + public Pair getFileAndLine(String className, int lineNumber) { + Stratum stratum = stratumManager.get(className); + if (stratum == null) { + return null; + } + Pair inputLine = stratum.getInputLine(lineNumber); + if (inputLine == null) { + return null; + } + return Pair.of(stratum.getSourceFile(inputLine.getLeft()), inputLine.getRight()); + } +} diff --git a/dd-java-agent/instrumentation/iast-instrumenter/src/main/java/datadog/trace/instrumentation/iastinstrumenter/StratumListener.java b/dd-java-agent/instrumentation/iast-instrumenter/src/main/java/datadog/trace/instrumentation/iastinstrumenter/StratumListener.java new file mode 100644 index 00000000000..cf03a62911f --- /dev/null +++ b/dd-java-agent/instrumentation/iast-instrumenter/src/main/java/datadog/trace/instrumentation/iastinstrumenter/StratumListener.java @@ -0,0 +1,24 @@ +package datadog.trace.instrumentation.iastinstrumenter; + +import datadog.trace.agent.tooling.bytebuddy.csi.Advices; +import datadog.trace.agent.tooling.bytebuddy.csi.ConstantPool; +import datadog.trace.agent.tooling.iast.stratum.StratumManager; +import javax.annotation.Nonnull; +import net.bytebuddy.description.type.TypeDescription; + +public class StratumListener implements Advices.Listener { + + public static final StratumListener INSTANCE = new StratumListener(); + + private StratumListener() { + // Prevent instantiation + } + + @Override + public void onConstantPool( + @Nonnull TypeDescription type, @Nonnull ConstantPool pool, byte[] classFile) { + if (StratumManager.shouldBeAnalyzed(type.getInternalName())) { + StratumManager.INSTANCE.analyzeClass(classFile); + } + } +} diff --git a/dd-java-agent/instrumentation/iast-instrumenter/src/test/groovy/datadog/trace/instrumentation/iastinstrumenter/SourceMapperImplTest.groovy b/dd-java-agent/instrumentation/iast-instrumenter/src/test/groovy/datadog/trace/instrumentation/iastinstrumenter/SourceMapperImplTest.groovy new file mode 100644 index 00000000000..9642bdf50b6 --- /dev/null +++ b/dd-java-agent/instrumentation/iast-instrumenter/src/test/groovy/datadog/trace/instrumentation/iastinstrumenter/SourceMapperImplTest.groovy @@ -0,0 +1,49 @@ +package datadog.trace.instrumentation.iastinstrumenter + +import datadog.trace.agent.test.AgentTestRunner +import datadog.trace.agent.tooling.iast.stratum.Stratum +import datadog.trace.agent.tooling.iast.stratum.StratumManager +import datadog.trace.api.Pair + +class SourceMapperImplTest extends AgentTestRunner { + + void 'test is disabled by default'(){ + when: + def instance = SourceMapperImpl.INSTANCE + + then: + instance == null + } + + void 'test getFileAndLine'(){ + setup: + final stratumManager = Mock(StratumManager) + final stratum = Mock(Stratum) + final sourceMapper = new SourceMapperImpl(stratumManager) + + when: + def result = sourceMapper.getFileAndLine("foo/bar/Baz", 42) + + then: "no stratum for this class" + 1 * stratumManager.get("foo/bar/Baz") >> null + result == null + + when: + result = sourceMapper.getFileAndLine("foo/bar/Baz", 42) + + then: "stratum exists but could not get input line number from stratum" + 1 * stratumManager.get("foo/bar/Baz") >> stratum + 1 * stratum.getInputLine(_) >> null + result == null + + when: + result = sourceMapper.getFileAndLine("foo/bar/Baz", 42) + + then: "stratum exists and input line number is found" + 1 * stratumManager.get("foo/bar/Baz") >> stratum + 1 * stratum.getInputLine(_) >> new Pair<>(1, 52) + 1 * stratum.getSourceFile(1) >> "foo/bar/Baz.jsp" + result.getLeft() == "foo/bar/Baz.jsp" + result.getRight() == 52 + } +} diff --git a/dd-smoke-tests/springboot-jetty-jsp/src/test/groovy/datadog/smoketest/springboot/IastSpringBootSmokeTest.groovy b/dd-smoke-tests/springboot-jetty-jsp/src/test/groovy/datadog/smoketest/springboot/IastSpringBootSmokeTest.groovy index 3f9277ce834..fae89b6cefd 100644 --- a/dd-smoke-tests/springboot-jetty-jsp/src/test/groovy/datadog/smoketest/springboot/IastSpringBootSmokeTest.groovy +++ b/dd-smoke-tests/springboot-jetty-jsp/src/test/groovy/datadog/smoketest/springboot/IastSpringBootSmokeTest.groovy @@ -7,6 +7,7 @@ import okhttp3.Response import static datadog.trace.api.config.IastConfig.IAST_DEBUG_ENABLED import static datadog.trace.api.config.IastConfig.IAST_DETECTION_MODE import static datadog.trace.api.config.IastConfig.IAST_ENABLED +import static datadog.trace.api.config.IastConfig.IAST_SOURCE_MAPPING_ENABLED class IastSpringBootSmokeTest extends AbstractIastServerSmokeTest { @@ -20,7 +21,8 @@ class IastSpringBootSmokeTest extends AbstractIastServerSmokeTest { command.addAll([ withSystemProperty(IAST_ENABLED, true), withSystemProperty(IAST_DETECTION_MODE, 'FULL'), - withSystemProperty(IAST_DEBUG_ENABLED, true) + withSystemProperty(IAST_DEBUG_ENABLED, true), + withSystemProperty(IAST_SOURCE_MAPPING_ENABLED, true) ]) command.addAll((String[]) ['-jar', springBootWar, "--server.port=${httpPort}"]) ProcessBuilder processBuilder = new ProcessBuilder(command) @@ -40,7 +42,9 @@ class IastSpringBootSmokeTest extends AbstractIastServerSmokeTest { then: response.successful hasVulnerability { vul -> - vul.type == 'XSS' + vul.type == 'XSS' && + vul.location.path == 'WEB-INF/jsp/test_xss.jsp' && + vul.location.line == 9 } } } diff --git a/dd-smoke-tests/springboot-tomcat-jsp/src/test/groovy/datadog/smoketest/springboot/IastSpringBootSmokeTest.groovy b/dd-smoke-tests/springboot-tomcat-jsp/src/test/groovy/datadog/smoketest/springboot/IastSpringBootSmokeTest.groovy index 3f9277ce834..fae89b6cefd 100644 --- a/dd-smoke-tests/springboot-tomcat-jsp/src/test/groovy/datadog/smoketest/springboot/IastSpringBootSmokeTest.groovy +++ b/dd-smoke-tests/springboot-tomcat-jsp/src/test/groovy/datadog/smoketest/springboot/IastSpringBootSmokeTest.groovy @@ -7,6 +7,7 @@ import okhttp3.Response import static datadog.trace.api.config.IastConfig.IAST_DEBUG_ENABLED import static datadog.trace.api.config.IastConfig.IAST_DETECTION_MODE import static datadog.trace.api.config.IastConfig.IAST_ENABLED +import static datadog.trace.api.config.IastConfig.IAST_SOURCE_MAPPING_ENABLED class IastSpringBootSmokeTest extends AbstractIastServerSmokeTest { @@ -20,7 +21,8 @@ class IastSpringBootSmokeTest extends AbstractIastServerSmokeTest { command.addAll([ withSystemProperty(IAST_ENABLED, true), withSystemProperty(IAST_DETECTION_MODE, 'FULL'), - withSystemProperty(IAST_DEBUG_ENABLED, true) + withSystemProperty(IAST_DEBUG_ENABLED, true), + withSystemProperty(IAST_SOURCE_MAPPING_ENABLED, true) ]) command.addAll((String[]) ['-jar', springBootWar, "--server.port=${httpPort}"]) ProcessBuilder processBuilder = new ProcessBuilder(command) @@ -40,7 +42,9 @@ class IastSpringBootSmokeTest extends AbstractIastServerSmokeTest { then: response.successful hasVulnerability { vul -> - vul.type == 'XSS' + vul.type == 'XSS' && + vul.location.path == 'WEB-INF/jsp/test_xss.jsp' && + vul.location.line == 9 } } } diff --git a/dd-trace-api/src/main/java/datadog/trace/api/config/IastConfig.java b/dd-trace-api/src/main/java/datadog/trace/api/config/IastConfig.java index cd6ae363226..973d706a6f1 100644 --- a/dd-trace-api/src/main/java/datadog/trace/api/config/IastConfig.java +++ b/dd-trace-api/src/main/java/datadog/trace/api/config/IastConfig.java @@ -23,6 +23,8 @@ public final class IastConfig { public static final String IAST_TRUNCATION_MAX_VALUE_LENGTH = "iast.truncation.max.value.length"; public static final String IAST_CONTEXT_MODE = "iast.context.mode"; public static final String IAST_ANONYMOUS_CLASSES_ENABLED = "iast.anonymous-classes.enabled"; + public static final String IAST_SOURCE_MAPPING_ENABLED = "iast.source-mapping.enabled"; + public static final String IAST_SOURCE_MAPPING_MAX_SIZE = "iast.source-mapping.max-size"; private IastConfig() {} } diff --git a/internal-api/src/main/java/datadog/trace/api/Config.java b/internal-api/src/main/java/datadog/trace/api/Config.java index e24c7413fcf..9ed2b362b2e 100644 --- a/internal-api/src/main/java/datadog/trace/api/Config.java +++ b/internal-api/src/main/java/datadog/trace/api/Config.java @@ -303,6 +303,8 @@ import static datadog.trace.api.config.IastConfig.IAST_REDACTION_ENABLED; import static datadog.trace.api.config.IastConfig.IAST_REDACTION_NAME_PATTERN; import static datadog.trace.api.config.IastConfig.IAST_REDACTION_VALUE_PATTERN; +import static datadog.trace.api.config.IastConfig.IAST_SOURCE_MAPPING_ENABLED; +import static datadog.trace.api.config.IastConfig.IAST_SOURCE_MAPPING_MAX_SIZE; import static datadog.trace.api.config.IastConfig.IAST_STACKTRACE_LEAK_SUPPRESS; import static datadog.trace.api.config.IastConfig.IAST_TELEMETRY_VERBOSITY; import static datadog.trace.api.config.IastConfig.IAST_TRUNCATION_MAX_VALUE_LENGTH; @@ -779,6 +781,8 @@ static class HostNameHolder { private final IastContext.Mode iastContextMode; private final boolean iastHardcodedSecretEnabled; private final boolean iastAnonymousClassesEnabled; + private final boolean iastSourceMappingEnabled; + private final int iastSourceMappingMaxSize; private final boolean ciVisibilityTraceSanitationEnabled; private final boolean ciVisibilityAgentlessEnabled; @@ -1751,6 +1755,8 @@ PROFILING_DATADOG_PROFILER_ENABLED, isDatadogProfilerSafeInCurrentEnvironment()) iastAnonymousClassesEnabled = configProvider.getBoolean( IAST_ANONYMOUS_CLASSES_ENABLED, DEFAULT_IAST_ANONYMOUS_CLASSES_ENABLED); + iastSourceMappingEnabled = configProvider.getBoolean(IAST_SOURCE_MAPPING_ENABLED, false); + iastSourceMappingMaxSize = configProvider.getInteger(IAST_SOURCE_MAPPING_MAX_SIZE, 1000); ciVisibilityTraceSanitationEnabled = configProvider.getBoolean(CIVISIBILITY_TRACE_SANITATION_ENABLED, true); @@ -2987,6 +2993,14 @@ public boolean isIastHardcodedSecretEnabled() { return iastHardcodedSecretEnabled; } + public boolean isIastSourceMappingEnabled() { + return iastSourceMappingEnabled; + } + + public int getIastSourceMappingMaxSize() { + return iastSourceMappingMaxSize; + } + public IastDetectionMode getIastDetectionMode() { return iastDetectionMode; } diff --git a/internal-api/src/main/java/datadog/trace/api/iast/stratum/SourceMapper.java b/internal-api/src/main/java/datadog/trace/api/iast/stratum/SourceMapper.java new file mode 100644 index 00000000000..f72ac131f3d --- /dev/null +++ b/internal-api/src/main/java/datadog/trace/api/iast/stratum/SourceMapper.java @@ -0,0 +1,8 @@ +package datadog.trace.api.iast.stratum; + +import datadog.trace.api.Pair; + +public interface SourceMapper { + + Pair getFileAndLine(String className, int lineNumber); +} diff --git a/internal-api/src/main/java/datadog/trace/api/iast/telemetry/IastMetric.java b/internal-api/src/main/java/datadog/trace/api/iast/telemetry/IastMetric.java index 0421715bc51..6e2942b3a03 100644 --- a/internal-api/src/main/java/datadog/trace/api/iast/telemetry/IastMetric.java +++ b/internal-api/src/main/java/datadog/trace/api/iast/telemetry/IastMetric.java @@ -19,7 +19,9 @@ public enum IastMetric { EXECUTED_TAINTED("executed.tainted", true, Scope.REQUEST, Verbosity.DEBUG), REQUEST_TAINTED("request.tainted", true, Scope.REQUEST, Verbosity.INFORMATION), TAINTED_FLAT_MODE("tainted.flat.mode", false, Scope.GLOBAL, Verbosity.INFORMATION), - JSON_TAG_SIZE_EXCEED("json.tag.size.exceeded", true, Scope.GLOBAL, Verbosity.INFORMATION); + JSON_TAG_SIZE_EXCEED("json.tag.size.exceeded", true, Scope.GLOBAL, Verbosity.INFORMATION), + SOURCE_MAPPING_LIMIT_REACHED( + "source.mapping.limit.reached", true, Scope.GLOBAL, Verbosity.INFORMATION); private static final int COUNT;