Skip to content

Commit

Permalink
Remove callsites for IAST sources and use bytebuddy advices (#6083)
Browse files Browse the repository at this point in the history
  • Loading branch information
manuel-alvarez-alvarez authored Oct 31, 2023
1 parent 205e504 commit 4ad36f5
Show file tree
Hide file tree
Showing 46 changed files with 1,929 additions and 2,133 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package datadog.trace.agent.tooling.iast;

import datadog.trace.api.iast.IastContext;
import datadog.trace.api.iast.propagation.PropagationModule;
import datadog.trace.util.stacktrace.StackUtils;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.util.Enumeration;
import javax.annotation.Nullable;

public class TaintableEnumeration implements Enumeration<String> {

private static final String CLASS_NAME = TaintableEnumeration.class.getName();

private volatile IastContext context;
private volatile boolean contextFetched;

private final PropagationModule module;

private final byte origin;

private final CharSequence name;

private final boolean useValueAsName;

private final Enumeration<String> delegate;

private TaintableEnumeration(
@NonNull final Enumeration<String> delegate,
@NonNull final PropagationModule module,
final byte origin,
@Nullable final CharSequence name,
final boolean useValueAsName) {
this.delegate = delegate;
this.module = module;
this.origin = origin;
this.name = name;
this.useValueAsName = useValueAsName;
}

@Override
public boolean hasMoreElements() {
try {
return delegate.hasMoreElements();
} catch (Throwable e) {
StackUtils.filterFirst(e, TaintableEnumeration::nonTaintableEnumerationStack);
throw e;
}
}

@Override
public String nextElement() {
final String next;
try {
next = delegate.nextElement();
} catch (Throwable e) {
StackUtils.filterFirst(e, TaintableEnumeration::nonTaintableEnumerationStack);
throw e;
}
try {
module.taint(context(), next, origin, name(next));
} catch (final Throwable e) {
module.onUnexpectedException("Failed to taint enumeration", e);
}
return next;
}

private IastContext context() {
if (!contextFetched) {
contextFetched = true;
context = IastContext.Provider.get();
}
return context;
}

private CharSequence name(final String value) {
if (name != null) {
return name;
}
return useValueAsName ? value : null;
}

private static boolean nonTaintableEnumerationStack(final StackTraceElement element) {
return !CLASS_NAME.equals(element.getClassName());
}

public static Enumeration<String> wrap(
@NonNull final Enumeration<String> delegate,
@NonNull final PropagationModule module,
final byte origin,
@Nullable final CharSequence name) {
return new TaintableEnumeration(delegate, module, origin, name, false);
}

public static Enumeration<String> wrap(
@NonNull final Enumeration<String> delegate,
@NonNull final PropagationModule module,
final byte origin,
boolean useValueAsName) {
return new TaintableEnumeration(delegate, module, origin, null, useValueAsName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,6 @@
2 org.springframework.expression.*
2 org.springframework.format.*
2 org.springframework.http.*
# Need for IAST: calls ServletRequest methods instrumented at callsite
0 org.springframework.http.server.ServletServerHttpRequest
# Need for IAST: we instrument these classes
0 org.springframework.http.HttpHeaders
0 org.springframework.http.ReadOnlyHttpHeaders
Expand Down Expand Up @@ -321,14 +319,11 @@
2 org.springframework.validation.*
2 org.springframework.web.*
0 org.springframework.web.context.request.async.*
0 org.springframework.web.context.request.*
0 org.springframework.web.context.support.AbstractRefreshableWebApplicationContext
0 org.springframework.web.context.support.GenericWebApplicationContext
0 org.springframework.web.context.support.XmlWebApplicationContext
0 org.springframework.web.reactive.*
0 org.springframework.web.servlet.*
# Included for IAST
0 org.springframework.web.util.WebUtils
# Need for IAST so propagation of tainted objects is complete in spring 2.7.5
0 org.springframework.util.StreamUtils$NonClosingInputStream
# Included for IAST Spring mvc unvalidated redirect and xss vulnerability
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package datadog.trace.agent.tooling.iast

import datadog.trace.api.gateway.RequestContext
import datadog.trace.api.gateway.RequestContextSlot
import datadog.trace.api.iast.IastContext
import datadog.trace.api.iast.InstrumentationBridge
import datadog.trace.api.iast.SourceTypes
import datadog.trace.api.iast.propagation.PropagationModule
import datadog.trace.bootstrap.instrumentation.api.AgentSpan
import datadog.trace.bootstrap.instrumentation.api.AgentTracer
import datadog.trace.test.util.DDSpecification
import spock.lang.Shared

class TaintableEnumerationTest extends DDSpecification {

@Shared
protected static final AgentTracer.TracerAPI ORIGINAL_TRACER = AgentTracer.get()

protected AgentTracer.TracerAPI tracer = Mock(AgentTracer.TracerAPI)

protected IastContext iastCtx = Mock(IastContext)

protected RequestContext reqCtx = Mock(RequestContext) {
getData(RequestContextSlot.IAST) >> iastCtx
}

protected AgentSpan span = Mock(AgentSpan) {
getRequestContext() >> reqCtx
}

protected PropagationModule module


void setup() {
AgentTracer.forceRegister(tracer)
module = Mock(PropagationModule)
InstrumentationBridge.registerIastModule(module)
}

void cleanup() {
AgentTracer.forceRegister(ORIGINAL_TRACER)
InstrumentationBridge.clearIastModules()
}

void 'underlying enumerated values are tainted with a name'() {
given:
final values = (1..10).collect { "value$it".toString() }
final origin = SourceTypes.REQUEST_PARAMETER_NAME
final name = 'test'
final enumeration = TaintableEnumeration.wrap(Collections.enumeration(values), module, origin, name)

when:
final result = enumeration.collect()

then:
result == values
values.each { 1 * module.taint(_, it, origin, name) }
1 * tracer.activeSpan() >> span // only one access to the active context
}

void 'underlying enumerated values are tainted with the value as a name'() {
given:
final values = (1..10).collect { "value$it".toString() }
final origin = SourceTypes.REQUEST_PARAMETER_NAME
final enumeration = TaintableEnumeration.wrap(Collections.enumeration(values), module, origin, true)

when:
final result = enumeration.collect()

then:
result == values
values.each { 1 * module.taint(_, it, origin, it) }
}

void 'taintable enumeration leaves no trace in case of error'() {
given:
final origin = SourceTypes.REQUEST_PARAMETER_NAME
final enumeration = TaintableEnumeration.wrap(new BadEnumeration(), module, origin, true)

when:
enumeration.hasMoreElements()

then:
final first = thrown(Error)
first.stackTrace.find { it.className == TaintableEnumeration.name } == null
}

private static class BadEnumeration implements Enumeration<String> {
@Override
boolean hasMoreElements() {
throw new Error('Ooops!!!')
}

@Override
String nextElement() {
throw new Error('Boom!!!')
}
}
}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit 4ad36f5

Please sign in to comment.