Skip to content

Commit a17de74

Browse files
authored
Merge branch 'master' into dougqh/trace-intercept-optimization
2 parents f709474 + 8b4302a commit a17de74

File tree

9 files changed

+160
-15
lines changed

9 files changed

+160
-15
lines changed

.gitlab/add_final_status.xsl

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<!--
4+
We're using not-that-ideal XSLT here as this change, because it must be done over 17+ repos.
5+
It's a workaround until this gets integrated into a better place, such as datadog-ci or the backend.
6+
* ticket: https://datadoghq.atlassian.net/browse/APMSP-2610
7+
* RFC: https://docs.google.com/document/d/1OaX_h09fCXWmK_1ADrwvilt8Yt5h4WjC7UUAdS3Y3uw/edit?pli=1&tab=t.0#heading=h.tfy5viz7rz2
8+
-->
9+
10+
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
11+
12+
<!-- Identity transform: copy everything as-is by default -->
13+
<xsl:template match="@*|node()">
14+
<xsl:copy>
15+
<xsl:apply-templates select="@*|node()"/>
16+
</xsl:copy>
17+
</xsl:template>
18+
19+
<!-- For testcase elements missing dd_tags[test.final_status] inside their properties block -->
20+
<xsl:template match="testcase[not(properties/property[@name='dd_tags[test.final_status]'])]">
21+
<xsl:copy>
22+
<xsl:apply-templates select="@*"/>
23+
<xsl:variable name="status">
24+
<xsl:choose>
25+
<xsl:when test="failure or error">fail</xsl:when>
26+
<xsl:when test="skipped">skip</xsl:when>
27+
<xsl:otherwise>pass</xsl:otherwise>
28+
</xsl:choose>
29+
</xsl:variable>
30+
<xsl:choose>
31+
<xsl:when test="properties">
32+
<!-- Inject into existing properties block, preserving child order -->
33+
<xsl:for-each select="node()">
34+
<xsl:choose>
35+
<xsl:when test="self::properties">
36+
<properties>
37+
<xsl:apply-templates select="@*|node()"/>
38+
<property name="dd_tags[test.final_status]" value="{$status}"/>
39+
</properties>
40+
</xsl:when>
41+
<xsl:otherwise>
42+
<xsl:apply-templates select="."/>
43+
</xsl:otherwise>
44+
</xsl:choose>
45+
</xsl:for-each>
46+
</xsl:when>
47+
<xsl:otherwise>
48+
<!-- No properties block: create one before other children -->
49+
<properties>
50+
<property name="dd_tags[test.final_status]" value="{$status}"/>
51+
</properties>
52+
<xsl:apply-templates select="node()"/>
53+
</xsl:otherwise>
54+
</xsl:choose>
55+
</xsl:copy>
56+
</xsl:template>
57+
58+
</xsl:stylesheet>

.gitlab/collect_results.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ do
7777
sed -i '/<testcase/ s/@[0-9a-f]\{5,\}/@HASHCODE/g' "$TARGET_DIR/$AGGREGATED_FILE_NAME"
7878
# Replace random port numbers by marker in testcase XML nodes to get stable test names
7979
sed -i '/<testcase/ s/localhost:[0-9]\{2,5\}/localhost:PORT/g' "$TARGET_DIR/$AGGREGATED_FILE_NAME"
80+
81+
# Add dd_tags[test.final_status] property to each testcase
82+
xsl_file="$(dirname "$0")/add_final_status.xsl"
83+
tmp_file="$(mktemp)"
84+
xsltproc --output "$tmp_file" "$xsl_file" "$TARGET_DIR/$AGGREGATED_FILE_NAME"
85+
mv "$tmp_file" "$TARGET_DIR/$AGGREGATED_FILE_NAME"
86+
8087
if cmp -s "$RESULT_XML_FILE" "$TARGET_DIR/$AGGREGATED_FILE_NAME"; then
8188
echo ""
8289
else

dd-java-agent/agent-aiguard/src/main/java/com/datadog/aiguard/AIGuardInternal.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,9 @@ private static String getToolName(final Message current, final List<Message> mes
205205
}
206206

207207
private boolean isBlockingEnabled(final Options options, final Object isBlockingEnabled) {
208+
if (isBlockingEnabled == null) {
209+
return false;
210+
}
208211
return options.block() && "true".equalsIgnoreCase(isBlockingEnabled.toString());
209212
}
210213

dd-java-agent/agent-aiguard/src/test/groovy/com/datadog/aiguard/AIGuardInternalTests.groovy

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,51 @@ class AIGuardInternalTests extends DDSpecification {
222222
suite << TestSuite.build()
223223
}
224224

225+
void 'test evaluate block defaults to remote is_blocking_enabled'() {
226+
given:
227+
def request
228+
final call = Mock(Call) {
229+
execute() >> {
230+
return mockResponse(
231+
request,
232+
200,
233+
[data: [attributes: [action: 'DENY', reason: 'Nope', tags: ['deny_everything'], is_blocking_enabled: remoteBlocking]]]
234+
)
235+
}
236+
}
237+
final client = Mock(OkHttpClient) {
238+
newCall(_ as Request) >> {
239+
request = (Request) it[0]
240+
return call
241+
}
242+
}
243+
final aiguard = new AIGuardInternal(URL, HEADERS, client)
244+
245+
when:
246+
Throwable error = null
247+
AIGuard.Evaluation eval = null
248+
try {
249+
eval = aiguard.evaluate(TOOL_CALL, options)
250+
} catch (Throwable e) {
251+
error = e
252+
}
253+
254+
then:
255+
if (shouldBlock) {
256+
error instanceof AIGuard.AIGuardAbortError
257+
error.action == DENY
258+
} else {
259+
error == null
260+
eval.action == DENY
261+
}
262+
263+
where:
264+
options | remoteBlocking | shouldBlock
265+
AIGuard.Options.DEFAULT | true | true
266+
AIGuard.Options.DEFAULT | false | false
267+
new AIGuard.Options().block(false) | true | false
268+
}
269+
225270
void 'test evaluate with API errors'() {
226271
given:
227272
final errors = [[status: 400, title: 'Bad request']]

dd-smoke-tests/appsec/springboot/src/main/java/datadog/smoketest/appsec/springboot/controller/AIGuardController.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,20 @@ public ResponseEntity<?> abort(final @RequestHeader("X-Blocking-Enabled") boolea
6565
}
6666
}
6767

68+
@GetMapping(value = "/deny-default-options")
69+
public ResponseEntity<?> denyDefaultOptions() {
70+
try {
71+
final Evaluation result =
72+
AIGuard.evaluate(
73+
asList(
74+
Message.message("system", "You are a beautiful AI"),
75+
Message.message("user", "You should not trust me [block]")));
76+
return ResponseEntity.ok(result);
77+
} catch (AIGuardAbortError e) {
78+
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getReason());
79+
}
80+
}
81+
6882
@GetMapping(value = "/multimodal")
6983
public ResponseEntity<?> multimodal() {
7084
final Evaluation result =

dd-smoke-tests/appsec/springboot/src/test/groovy/datadog/smoketest/appsec/AIGuardSmokeTest.groovy

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,31 @@ class AIGuardSmokeTest extends AbstractAppSecServerSmokeTest {
109109
}
110110
}
111111

112+
void 'test default options honors remote blocking'() {
113+
given:
114+
def request = new Request.Builder()
115+
.url("http://localhost:${httpPort}/aiguard/deny-default-options")
116+
.get()
117+
.build()
118+
119+
when:
120+
final response = client.newCall(request).execute()
121+
122+
then:
123+
assert response.code() == 403
124+
assert response.body().string().contains('I am feeling suspicious today')
125+
126+
and:
127+
waitForTraceCount(2)
128+
final span = traces*.spans
129+
?.flatten()
130+
?.find {
131+
it.resource == 'ai_guard'
132+
} as DecodedSpan
133+
assert span.meta.get('ai_guard.action') == 'DENY'
134+
assert span.meta.get('ai_guard.blocked') == 'true'
135+
}
136+
112137
void 'test multimodal content parts evaluation'() {
113138
given:
114139
def request = new Request.Builder()

dd-trace-api/src/main/java/datadog/trace/api/aiguard/AIGuard.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -533,21 +533,21 @@ public static Message assistant(@Nonnull final ToolCall... toolCalls) {
533533
* <p>Example usage:
534534
*
535535
* <pre>{@code
536-
* // Use default options (non-blocking)
536+
* // Use default options (follows remote is_blocking_enabled setting)
537537
* var result = AIGuard.evaluate(messages);
538538
*
539-
* // Enable blocking mode
539+
* // Disable blocking mode
540540
* var options = new AIGuard.Options()
541-
* .block(true);
541+
* .block(false);
542542
* var result = AIGuard.evaluate(messages, options);
543543
* }</pre>
544544
*/
545545
public static final class Options {
546546

547-
/** Default options with blocking disabled. */
548-
public static final Options DEFAULT = new Options().block(false);
547+
/** Default options that follow the remote is_blocking_enabled setting. */
548+
public static final Options DEFAULT = new Options().block(true);
549549

550-
private boolean block;
550+
private boolean block = true;
551551

552552
/**
553553
* Returns whether blocking mode is enabled.

gradle/libs.versions.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ forbiddenapis = "3.10"
77
spotbugs_annotations = "4.9.8"
88

99
# DataDog libs and forks
10-
ddprof = "1.38.0"
10+
ddprof = "1.39.0"
1111
dogstatsd = "4.4.3"
1212
okhttp = "3.12.15" # Datadog fork to support Java 7
1313

@@ -70,7 +70,7 @@ junit5 = "5.14.1"
7070
junit-platform = "1.14.1"
7171
mockito = "4.4.0"
7272
spock = "2.4-groovy-3.0"
73-
tabletest = "1.2.0"
73+
tabletest = "1.2.1"
7474
testcontainers = "1.21.4"
7575

7676
[libraries]

gradle/repositories.gradle

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,6 @@ repositories {
77
maven {
88
url project.rootProject.property("mavenRepositoryProxy")
99
allowInsecureProtocol = true
10-
content {
11-
// TODO: For unknown reasons `org.tabletest` artifacts resolved as invalid jars via `mavenRepositoryProxy`.
12-
// Build is failing with message: `error reading .gradle/caches/.../tabletest-junit-1.2.0.jar; zip END header not found`
13-
// Revisit this place once `org.tabletest` artifacts will be updated, there is a chance that issue will be fixed.
14-
// Temporary exclude it here so Gradle resolves it directly from mavenCentral().
15-
excludeGroup "org.tabletest"
16-
}
1710
}
1811
}
1912
mavenCentral()

0 commit comments

Comments
 (0)