Skip to content

Commit d4d2069

Browse files
jandro996devflow.devflow-routing-intake
andauthored
Add RASP instrumentation for Files.copy(Path,Path) and Files.copy(Path,OutputStream) (#11179)
# What Does This Do - Instruments `Files.copy(Path source, Path target, CopyOption[])` in `FilesCallSite`: fires `beforeFileLoaded(source)` for LFI detection and `beforeFileWritten(target)` for write detection - Instruments `Files.copy(Path source, OutputStream out)` in `FilesCallSite`: fires `beforeFileLoaded(source)` for LFI detection - Adds `copyPathToPath` and `copyToStream` helpers in `TestFilesSuite` and corresponding Spock tests in `FilesCallSiteTest` # Motivation `FilesCallSite` was introduced in #11113 but omitted the `Files.copy(Path, Path, CopyOption[])` and `Files.copy(Path, OutputStream)` overloads. Both are common file-management APIs: the source path is an LFI attack vector and the target path (in the path-to-path variant) is a path-traversal write vector. This PR closes that gap. # Additional Notes # Contributor Checklist - Format the title according to [the contribution guidelines](https://github.com/DataDog/dd-trace-java/blob/master/CONTRIBUTING.md#title-format) - Assign the `type:` and (`comp:` or `inst:`) labels in addition to [any other useful labels](https://github.com/DataDog/dd-trace-java/blob/master/CONTRIBUTING.md#labels) - Avoid using `close`, `fix`, or [any linking keywords](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword) when referencing an issue Use `solves` instead, and assign the PR [milestone](https://github.com/DataDog/dd-trace-java/milestones) to the issue - Update the [CODEOWNERS](https://github.com/DataDog/dd-trace-java/blob/master/.github/CODEOWNERS) file on source file addition, migration, or deletion - Update [public documentation](https://docs.datadoghq.com/tracing/trace_collection/library_config/java/) with any new configuration flags or behaviors Jira ticket: [APPSEC-61874](https://datadoghq.atlassian.net/browse/APPSEC-61874) ***Note:*** **Once your PR is ready to merge, add it to the merge queue by commenting `/merge`.** `/merge -c` cancels the queue request. `/merge -f --reason "reason"` skips all merge queue checks; please use this judiciously, as some checks do not run at the PR-level. For more information, see [this doc](https://datadoghq.atlassian.net/wiki/spaces/DEVX/pages/3121612126/MergeQueue). Co-authored-by: devflow.devflow-routing-intake <devflow.devflow-routing-intake@kubernetes.us1.ddbuild.io>
1 parent 93780c1 commit d4d2069

3 files changed

Lines changed: 58 additions & 0 deletions

File tree

dd-java-agent/instrumentation/java/java-io-1.8/src/main/java/datadog/trace/instrumentation/java/lang/FilesCallSite.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,19 @@ public static void beforeCopyFromStream(@CallSite.Argument(1) @Nullable final Pa
4343
}
4444
}
4545

46+
@CallSite.Before(
47+
"java.nio.file.Path java.nio.file.Files.copy(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption[])")
48+
public static void beforeCopyPathToPath(
49+
@CallSite.Argument(0) @Nullable final Path source,
50+
@CallSite.Argument(1) @Nullable final Path target) {
51+
if (source != null) {
52+
FileIORaspHelper.INSTANCE.beforeFileLoaded(source.toString());
53+
}
54+
if (target != null) {
55+
FileIORaspHelper.INSTANCE.beforeFileWritten(target.toString());
56+
}
57+
}
58+
4659
@CallSite.Before(
4760
"java.nio.file.Path java.nio.file.Files.move(java.nio.file.Path, java.nio.file.Path, java.nio.file.CopyOption[])")
4861
public static void beforeMove(@CallSite.Argument(1) @Nullable final Path target) {
@@ -51,6 +64,13 @@ public static void beforeMove(@CallSite.Argument(1) @Nullable final Path target)
5164
}
5265
}
5366

67+
@CallSite.Before("long java.nio.file.Files.copy(java.nio.file.Path, java.io.OutputStream)")
68+
public static void beforeCopyToStream(@CallSite.Argument(0) @Nullable final Path source) {
69+
if (source != null) {
70+
FileIORaspHelper.INSTANCE.beforeFileLoaded(source.toString());
71+
}
72+
}
73+
5474
// ===================== READ =====================
5575

5676
@CallSite.Before(

dd-java-agent/instrumentation/java/java-io-1.8/src/test/groovy/datadog/trace/instrumentation/java/io/FilesCallSiteTest.groovy

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,35 @@ class FilesCallSiteTest extends BaseIoRaspCallSiteTest {
102102
1 * helper.beforeFileWritten(path.toString())
103103
}
104104

105+
void 'test RASP Files.copy path to path fires beforeFileLoaded on source and beforeFileWritten on target'() {
106+
setup:
107+
final helper = Mock(FileIORaspHelper)
108+
FileIORaspHelper.INSTANCE = helper
109+
final source = newFile('test_rasp_copy_src.txt').toPath()
110+
final target = temporaryFolder.resolve('test_rasp_copy_path_dst.txt')
111+
112+
when:
113+
TestFilesSuite.copyPathToPath(source, target)
114+
115+
then:
116+
1 * helper.beforeFileLoaded(source.toString())
117+
1 * helper.beforeFileWritten(target.toString())
118+
}
119+
120+
void 'test RASP Files.copy path to OutputStream fires beforeFileLoaded on source'() {
121+
setup:
122+
final helper = Mock(FileIORaspHelper)
123+
FileIORaspHelper.INSTANCE = helper
124+
final source = newFile('test_rasp_copy_stream_src.txt').toPath()
125+
126+
when:
127+
TestFilesSuite.copyToStream(source, new ByteArrayOutputStream())
128+
129+
then:
130+
1 * helper.beforeFileLoaded(source.toString())
131+
0 * helper.beforeFileWritten(_)
132+
}
133+
105134
void 'test RASP Files.move'() {
106135
setup:
107136
final helper = Mock(FileIORaspHelper)

dd-java-agent/instrumentation/java/java-io-1.8/src/test/java/foo/bar/TestFilesSuite.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,15 @@ public static BufferedWriter newBufferedWriterDefaultCharset(
5757
return Files.newBufferedWriter(path, options);
5858
}
5959

60+
public static Path copyPathToPath(
61+
final Path source, final Path target, final CopyOption... options) throws IOException {
62+
return Files.copy(source, target, options);
63+
}
64+
65+
public static long copyToStream(final Path source, final OutputStream out) throws IOException {
66+
return Files.copy(source, out);
67+
}
68+
6069
public static Path move(final Path source, final Path target, final CopyOption... options)
6170
throws IOException {
6271
return Files.move(source, target);

0 commit comments

Comments
 (0)