Skip to content

Commit 16c5238

Browse files
authored
m365_defender,sentinel_one_cloud_funnel: improve command line split script performance (#13335)
Investigation in the crowdstrike fdr data stream has shown that this processor can be significantly improved in terms of performance by reducing string allocation while tokenising the command line. This change replays the changes in that data stream in m365_defender and sentinel_one_cloud_funnel. ref: #13325
1 parent e224172 commit 16c5238

File tree

7 files changed

+71
-49
lines changed

7 files changed

+71
-49
lines changed

packages/m365_defender/changelog.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
# newer versions go on top
2+
- version: "3.1.0"
3+
changes:
4+
- description: Improve performance of event ingest pipeline.
5+
type: enhancement
6+
link: https://github.com/elastic/integrations/pull/13335
27
- version: "3.0.1"
38
changes:
49
- description: Fix `event.category` value for the AdvancedHunting-AlertInfo event.

packages/m365_defender/data_stream/event/elasticsearch/ingest_pipeline/pipeline_alert.yml

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -369,19 +369,19 @@ processors:
369369
return b;
370370
}
371371
372-
// readNextArg splits command line string cmd into next
373-
// argument and command line remainder.
374-
def readNextArg(String cmd) {
372+
// readNextArg splits command line string into next
373+
// argument and command line remainder offset.
374+
def readNextArg(String line, int offset) {
375375
def b = new StringBuilder();
376376
boolean inquote;
377377
int nslash;
378-
for (; cmd.length() > 0; cmd = cmd.substring(1)) {
379-
def c = cmd.charAt(0);
378+
for (; offset < line.length(); offset++) {
379+
def c = line.charAt(offset);
380380
if (c == (char)' ' || c == (char)0x09) {
381381
if (!inquote) {
382382
return [
383383
"arg": appendBSBytes(b, nslash).toString(),
384-
"rest": cmd.substring(1)
384+
"offset": offset+1
385385
];
386386
}
387387
} else if (c == (char)'"') {
@@ -390,9 +390,9 @@ processors:
390390
// use "Prior to 2008" rule from
391391
// http://daviddeley.com/autohotkey/parameters/parameters.htm
392392
// section 5.2 to deal with double double quotes
393-
if (inquote && cmd.length() > 1 && cmd.charAt(1) == (char)'"') {
393+
if (inquote && offset+1 < line.length() && line.charAt(offset+1) == (char)'"') {
394394
b.append(c);
395-
cmd = cmd.substring(1);
395+
offset++;
396396
}
397397
inquote = !inquote;
398398
} else {
@@ -409,24 +409,28 @@ processors:
409409
b.append(c);
410410
}
411411
return [
412-
"arg": appendBSBytes(b, nslash).toString(),
413-
"rest": ''
412+
"arg": appendBSBytes(b, nslash).toString(),
413+
"offset": line.length()
414414
];
415415
}
416416
417417
// commandLineToArgv splits a command line into individual argument
418418
// strings, following the Windows conventions documented
419419
// at http://daviddeley.com/autohotkey/parameters/parameters.htm#WINARGV
420420
// Original implementation found at: https://github.com/golang/go/commit/39c8d2b7faed06b0e91a1ad7906231f53aab45d1
421-
def commandLineToArgv(String cmd) {
421+
def commandLineToArgv(String line) {
422422
def args = new ArrayList();
423-
while (cmd.length() > 0) {
424-
if (cmd.charAt(0) == (char)' ' || cmd.charAt(0) == (char)0x09) {
425-
cmd = cmd.substring(1);
423+
for (int i = 0; i < line.length();) {
424+
if (line.charAt(i) == (char)' ' || line.charAt(i) == (char)0x09) {
425+
i++;
426+
continue;
427+
}
428+
def next = readNextArg(line, i);
429+
i = next.offset;
430+
if (next.arg == '') {
431+
// Empty strings will be removed later so don't bother adding them.
426432
continue;
427433
}
428-
def next = readNextArg(cmd);
429-
cmd = next.rest;
430434
args.add(next.arg);
431435
}
432436
return args;

packages/m365_defender/data_stream/event/elasticsearch/ingest_pipeline/pipeline_device.yml

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2628,19 +2628,19 @@ processors:
26282628
return b;
26292629
}
26302630
2631-
// readNextArg splits command line string cmd into next
2632-
// argument and command line remainder.
2633-
def readNextArg(String cmd) {
2631+
// readNextArg splits command line string into next
2632+
// argument and command line remainder offset.
2633+
def readNextArg(String line, int offset) {
26342634
def b = new StringBuilder();
26352635
boolean inquote;
26362636
int nslash;
2637-
for (; cmd.length() > 0; cmd = cmd.substring(1)) {
2638-
def c = cmd.charAt(0);
2637+
for (; offset < line.length(); offset++) {
2638+
def c = line.charAt(offset);
26392639
if (c == (char)' ' || c == (char)0x09) {
26402640
if (!inquote) {
26412641
return [
26422642
"arg": appendBSBytes(b, nslash).toString(),
2643-
"rest": cmd.substring(1)
2643+
"offset": offset+1
26442644
];
26452645
}
26462646
} else if (c == (char)'"') {
@@ -2649,9 +2649,9 @@ processors:
26492649
// use "Prior to 2008" rule from
26502650
// http://daviddeley.com/autohotkey/parameters/parameters.htm
26512651
// section 5.2 to deal with double double quotes
2652-
if (inquote && cmd.length() > 1 && cmd.charAt(1) == (char)'"') {
2652+
if (inquote && offset+1 < line.length() && line.charAt(offset+1) == (char)'"') {
26532653
b.append(c);
2654-
cmd = cmd.substring(1);
2654+
offset++;
26552655
}
26562656
inquote = !inquote;
26572657
} else {
@@ -2668,24 +2668,28 @@ processors:
26682668
b.append(c);
26692669
}
26702670
return [
2671-
"arg": appendBSBytes(b, nslash).toString(),
2672-
"rest": ''
2671+
"arg": appendBSBytes(b, nslash).toString(),
2672+
"offset": line.length()
26732673
];
26742674
}
26752675
26762676
// commandLineToArgv splits a command line into individual argument
26772677
// strings, following the Windows conventions documented
26782678
// at http://daviddeley.com/autohotkey/parameters/parameters.htm#WINARGV
26792679
// Original implementation found at: https://github.com/golang/go/commit/39c8d2b7faed06b0e91a1ad7906231f53aab45d1
2680-
def commandLineToArgv(String cmd) {
2680+
def commandLineToArgv(String line) {
26812681
def args = new ArrayList();
2682-
while (cmd.length() > 0) {
2683-
if (cmd.charAt(0) == (char)' ' || cmd.charAt(0) == (char)0x09) {
2684-
cmd = cmd.substring(1);
2682+
for (int i = 0; i < line.length();) {
2683+
if (line.charAt(i) == (char)' ' || line.charAt(i) == (char)0x09) {
2684+
i++;
2685+
continue;
2686+
}
2687+
def next = readNextArg(line, i);
2688+
i = next.offset;
2689+
if (next.arg == '') {
2690+
// Empty strings will be removed later so don't bother adding them.
26852691
continue;
26862692
}
2687-
def next = readNextArg(cmd);
2688-
cmd = next.rest;
26892693
args.add(next.arg);
26902694
}
26912695
return args;

packages/m365_defender/manifest.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
format_version: "3.2.3"
22
name: m365_defender
33
title: Microsoft M365 Defender
4-
version: "3.0.1"
4+
version: "3.1.0"
55
description: Collect logs from Microsoft M365 Defender with Elastic Agent.
66
categories:
77
- "security"

packages/sentinel_one_cloud_funnel/changelog.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
# newer versions go on top
2+
- version: "1.11.0"
3+
changes:
4+
- description: Improve performance of event ingest pipeline.
5+
type: enhancement
6+
link: https://github.com/elastic/integrations/pull/13335
27
- version: "1.10.0"
38
changes:
49
- description: Add support to configure start_timestamp and ignore_older configurations for AWS S3 backed inputs.

packages/sentinel_one_cloud_funnel/data_stream/event/elasticsearch/ingest_pipeline/default.yml

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2775,19 +2775,19 @@ processors:
27752775
return b;
27762776
}
27772777
2778-
// readNextArg splits command line string cmd into next
2779-
// argument and command line remainder.
2780-
def readNextArg(String cmd) {
2778+
// readNextArg splits command line string into next
2779+
// argument and command line remainder offset.
2780+
def readNextArg(String line, int offset) {
27812781
def b = new StringBuilder();
27822782
boolean inquote;
27832783
int nslash;
2784-
for (; cmd.length() > 0; cmd = cmd.substring(1)) {
2785-
def c = cmd.charAt(0);
2784+
for (; offset < line.length(); offset++) {
2785+
def c = line.charAt(offset);
27862786
if (c == (char)' ' || c == (char)0x09) {
27872787
if (!inquote) {
27882788
return [
27892789
"arg": appendBSBytes(b, nslash).toString(),
2790-
"rest": cmd.substring(1)
2790+
"offset": offset+1
27912791
];
27922792
}
27932793
} else if (c == (char)'"') {
@@ -2796,9 +2796,9 @@ processors:
27962796
// use "Prior to 2008" rule from
27972797
// http://daviddeley.com/autohotkey/parameters/parameters.htm
27982798
// section 5.2 to deal with double double quotes
2799-
if (inquote && cmd.length() > 1 && cmd.charAt(1) == (char)'"') {
2799+
if (inquote && offset+1 < line.length() && line.charAt(offset+1) == (char)'"') {
28002800
b.append(c);
2801-
cmd = cmd.substring(1);
2801+
offset++;
28022802
}
28032803
inquote = !inquote;
28042804
} else {
@@ -2816,23 +2816,27 @@ processors:
28162816
}
28172817
return [
28182818
"arg": appendBSBytes(b, nslash).toString(),
2819-
"rest": ''
2819+
"offset": line.length()
28202820
];
28212821
}
28222822
28232823
// commandLineToArgv splits a command line into individual argument
28242824
// strings, following the Windows conventions documented
28252825
// at http://daviddeley.com/autohotkey/parameters/parameters.htm#WINARGV
28262826
// Original implementation found at: https://github.com/golang/go/commit/39c8d2b7faed06b0e91a1ad7906231f53aab45d1
2827-
def commandLineToArgv(String cmd) {
2827+
def commandLineToArgv(String line) {
28282828
def args = new ArrayList();
2829-
while (cmd.length() > 0) {
2830-
if (cmd.charAt(0) == (char)' ' || cmd.charAt(0) == (char)0x09) {
2831-
cmd = cmd.substring(1);
2829+
for (int i = 0; i < line.length();) {
2830+
if (line.charAt(i) == (char)' ' || line.charAt(i) == (char)0x09) {
2831+
i++;
2832+
continue;
2833+
}
2834+
def next = readNextArg(line, i);
2835+
i = next.offset;
2836+
if (next.arg == '') {
2837+
// Empty strings will be removed later so don't bother adding them.
28322838
continue;
28332839
}
2834-
def next = readNextArg(cmd);
2835-
cmd = next.rest;
28362840
args.add(next.arg);
28372841
}
28382842
return args;

packages/sentinel_one_cloud_funnel/manifest.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
format_version: "3.0.2"
22
name: sentinel_one_cloud_funnel
33
title: SentinelOne Cloud Funnel
4-
version: "1.10.0"
4+
version: "1.11.0"
55
description: Collect logs from SentinelOne Cloud Funnel with Elastic Agent.
66
type: integration
77
categories: ["security", "edr_xdr"]

0 commit comments

Comments
 (0)