Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ public interface VulnerabilityType {
/** A flag to indicate if the vulnerability is deduplicable. */
boolean isDeduplicable();

byte type();

static Builder type(final byte type) {
return new Builder(type);
}
Expand Down Expand Up @@ -191,6 +193,11 @@ public boolean isDeduplicable() {
return deduplicable;
}

@Override
public byte type() {
return type;
}

/** Useful for troubleshooting issues when vulns are serialized without moshi */
public String getName() {
return name();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static com.datadog.iast.util.ObjectVisitor.State.CONTINUE;
import static com.datadog.iast.util.ObjectVisitor.State.EXIT;
import static datadog.trace.api.iast.VulnerabilityMarks.CUSTOM_SECURITY_CONTROL_MARK;

import com.datadog.iast.Dependencies;
import com.datadog.iast.Reporter;
Expand All @@ -22,6 +23,8 @@
import datadog.trace.api.Pair;
import datadog.trace.api.iast.IastContext;
import datadog.trace.api.iast.Taintable;
import datadog.trace.api.iast.telemetry.IastMetric;
import datadog.trace.api.iast.telemetry.IastMetricCollector;
import datadog.trace.bootstrap.instrumentation.api.AgentSpan;
import datadog.trace.bootstrap.instrumentation.api.AgentTracer;
import datadog.trace.instrumentation.iastinstrumenter.IastExclusionTrie;
Expand Down Expand Up @@ -126,6 +129,7 @@ protected final Evidence checkInjection(
final TaintedObject tainted = origin == null ? null : to.get(origin);
if (origin != null && tainted != null) {
valueRanges = Ranges.getNotMarkedRanges(tainted.getRanges(), type.mark());
addSecurityControlMetrics(ctx, valueRanges, tainted.getRanges(), type);
value = origin;
} else {
valueRanges = Ranges.forObject((Source) taintable.$$DD$getSource(), type.mark());
Expand All @@ -137,6 +141,7 @@ protected final Evidence checkInjection(
return null;
}
valueRanges = Ranges.getNotMarkedRanges(tainted.getRanges(), type.mark());
addSecurityControlMetrics(ctx, valueRanges, tainted.getRanges(), type);
}

if (valueRanges == null || valueRanges.length == 0) {
Expand All @@ -160,6 +165,25 @@ protected final Evidence checkInjection(
return report(span, type, evidence, ranges, locationSupplier);
}

private void addSecurityControlMetrics(
@Nonnull final IastContext ctx,
@Nullable final Range[] valueRanges,
@Nonnull final Range[] taintedRanges,
@Nonnull final VulnerabilityType type) {
if ((valueRanges != null
&& valueRanges.length
!= 0) // ranges without the vulnerability mark implies vulnerability
|| taintedRanges.length == 0 // no tainted ranges
) {
return;
}
// check if there are tainted ranges without the security control mark
Range[] marked = Ranges.getNotMarkedRanges(taintedRanges, CUSTOM_SECURITY_CONTROL_MARK);
if (marked == null || marked.length == 0) {
IastMetricCollector.add(IastMetric.SUPPRESSED_VULNERABILITIES, type.type(), 1, ctx);
}
}

@Nullable
protected final Evidence checkInjection(final VulnerabilityType type, final Iterator<?> items) {
return checkInjection(type, items, null, null);
Expand Down Expand Up @@ -207,6 +231,7 @@ protected final Evidence checkInjection(
Range[] valueRanges = null;
if (tainted != null) {
valueRanges = Ranges.getNotMarkedRanges(tainted.getRanges(), type.mark());
addSecurityControlMetrics(ctx, valueRanges, tainted.getRanges(), type);
}
addToEvidence(type, evidence, ranges, value, valueRanges, evidenceBuilder);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
package com.datadog.iast.securitycontrol

import com.datadog.iast.IastModuleImplTestBase
import com.datadog.iast.sink.XssModuleImpl
import static com.datadog.iast.taint.TaintUtils.addFromTaintFormat
import datadog.trace.api.iast.InstrumentationBridge
import datadog.trace.api.iast.VulnerabilityMarks
import static datadog.trace.api.iast.VulnerabilityMarks.NOT_MARKED
import datadog.trace.api.iast.VulnerabilityTypes
import datadog.trace.api.iast.propagation.PropagationModule
import datadog.trace.api.iast.securitycontrol.SecurityControl
import datadog.trace.api.iast.securitycontrol.SecurityControlFormatter
import datadog.trace.test.util.DDSpecification
import static datadog.trace.api.iast.telemetry.IastMetric.SUPPRESSED_VULNERABILITIES
import datadog.trace.api.iast.telemetry.IastMetricCollector
import foo.bar.securitycontrol.SecurityControlStaticTestSuite
import foo.bar.securitycontrol.SecurityControlTestSuite
import net.bytebuddy.agent.ByteBuddyAgent

import java.lang.instrument.Instrumentation

class IastSecurityControlTransformerForkedTest extends DDSpecification{
class IastSecurityControlTransformerForkedTest extends IastModuleImplTestBase{

//static methods
private static final String STATIC_SANITIZER = 'SANITIZER:XSS:foo.bar.securitycontrol.SecurityControlStaticTestSuite:sanitize'
Expand Down Expand Up @@ -43,6 +49,7 @@ class IastSecurityControlTransformerForkedTest extends DDSpecification{
private static final String INPUT_VALIDATOR_VALIDATE_SELECTED_LONG = 'INPUT_VALIDATOR:XSS:foo.bar.securitycontrol.SecurityControlTestSuite:validateSelectedLong:0'


private IastMetricCollector mockCollector

def setupSpec() {
final staticConfig = "${STATIC_SANITIZER};${STATIC_SANITIZE_VALIDATE_OBJECT};${STATIC_SANITIZE_INPUTS};${STATIC_SANITIZE_MANY_INPUTS};${STATIC_SANITIZE_INT};${STATIC_SANITIZE_LONG};${STATIC_INPUT_VALIDATOR_VALIDATE_ALL};${STATIC_INPUT_VALIDATOR_VALIDATE_OVERLOADED};${STATIC_INPUT_VALIDATOR_VALIDATE_RETURNING_INT};${STATIC_INPUT_VALIDATOR_VALIDATE_OBJECT};${STATIC_INPUT_VALIDATOR_VALIDATE_LONG};${STATIC_INPUT_VALIDATOR_VALIDATE_SELECTED_LONG}"
Expand All @@ -55,26 +62,31 @@ class IastSecurityControlTransformerForkedTest extends DDSpecification{
instrumentation.addTransformer(new IastSecurityControlTransformer(securityControls), true)
}

void setup() {
mockCollector = Mock(IastMetricCollector)
ctx.collector = mockCollector
}


void 'test sanitize'(){
given:
final iastModule = Mock(PropagationModule)
InstrumentationBridge.registerIastModule(iastModule)
final propagationModule = Mock(PropagationModule)
InstrumentationBridge.registerIastModule(propagationModule)
final marks = (VulnerabilityMarks.XSS_MARK | VulnerabilityMarks.CUSTOM_SECURITY_CONTROL_MARK)

when:
SecurityControlStaticTestSuite.&"$method".call(*args)

then:
expected * iastModule.markIfTainted( toSanitize, marks)
expected * propagationModule.markIfTainted( toSanitize, marks)
0 * _

when:
final suite = new SecurityControlTestSuite()
suite.&"$method".call(*args)

then:
expected * iastModule.markIfTainted( toSanitize, marks)
expected * propagationModule.markIfTainted( toSanitize, marks)
0 * _

where:
Expand All @@ -89,16 +101,16 @@ class IastSecurityControlTransformerForkedTest extends DDSpecification{

void 'test validate'(){
given:
final iastModule = Mock(PropagationModule)
InstrumentationBridge.registerIastModule(iastModule)
final propagationModule = Mock(PropagationModule)
InstrumentationBridge.registerIastModule(propagationModule)
final marks = (VulnerabilityMarks.XSS_MARK | VulnerabilityMarks.CUSTOM_SECURITY_CONTROL_MARK)

when:
SecurityControlStaticTestSuite.&"$method".call(*args)

then:
for (final validate : toValidate){
expected * iastModule.markIfTainted(validate, marks)
expected * propagationModule.markIfTainted(validate, marks)
}
0 * _

Expand All @@ -108,7 +120,7 @@ class IastSecurityControlTransformerForkedTest extends DDSpecification{

then:
for (final validate : toValidate){
expected * iastModule.markIfTainted(validate, marks)
expected * propagationModule.markIfTainted(validate, marks)
}
0 * _

Expand All @@ -128,4 +140,36 @@ class IastSecurityControlTransformerForkedTest extends DDSpecification{
'validateSelectedLong' | [1L] | args | 0
'validateSelectedLong' | [1L, 2L] | [args[0]] | 0
}

void 'test metrics'() {
setup:
final iastModule = new XssModuleImpl(dependencies)
final param = mapTainted(s, mark)

when:
iastModule.onXss(param as String)

then:
expected * mockCollector.addMetric(SUPPRESSED_VULNERABILITIES, VulnerabilityTypes.XSS, 1)

where:
s | mark | expected
null | NOT_MARKED | 0
'/var' | NOT_MARKED | 0
'/==>var<==' | NOT_MARKED | 0
'/==>var<==' | VulnerabilityMarks.XSS_MARK | 0
'/==>var<==' | VulnerabilityMarks.SQL_INJECTION_MARK | 0
'/==>var<==' | combine(VulnerabilityMarks.SQL_INJECTION_MARK, VulnerabilityMarks.CUSTOM_SECURITY_CONTROL_MARK) | 0
'/==>var<==' | combine(VulnerabilityMarks.XSS_MARK, VulnerabilityMarks.CUSTOM_SECURITY_CONTROL_MARK) | 1
}

private static int combine(int mark1, int mark2) {
return mark1 | mark2 // Perform the bitwise OR
}

private String mapTainted(final String value, final int mark) {
final result = addFromTaintFormat(ctx.taintedObjects, value, mark)
objectHolder.add(result)
return result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ public final class IastConfig {
public static final String IAST_EXPERIMENTAL_PROPAGATION_ENABLED =
"iast.experimental.propagation.enabled";
public static final String IAST_STACK_TRACE_ENABLED = "iast.stacktrace.enabled";
public static final String IAST_SECURITY_CONTROLS_ENABLED = "iast.security-controls.enabled";
public static final String IAST_SECURITY_CONTROLS_CONFIGURATION =
"iast.security-controls.configuration";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public enum IastMetric {
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),
SUPPRESSED_VULNERABILITIES(
"suppressed.vulnerabilities",
true,
Scope.REQUEST,
Tag.VULNERABILITY_TYPE,
Verbosity.INFORMATION),
EXPERIMENTAL_PROPAGATION("experimental.propagation", false, Scope.GLOBAL, Verbosity.INFORMATION);

private static final int COUNT;
Expand Down
Loading