[windows] Fix PowerShell 4103 ContextInfo parsing for indented multi-line values - #20693
Conversation
…line values ContextInfo is a block of indented "<Key> = <Value>" lines. The value of Host Application is the command line of the PowerShell host process. If a user starts PowerShell with -Command <script>, the full script goes into that value, and the script keeps its line breaks and its indents. The kv processor made a new key from each indented line. If such a line contained no ":" and no "=", the processor failed. Because the kv was the first processor and had no ignore_failure, the failure stopped the full pipeline. All subsequent processors did not run, which included the @Custom pipeline that Fleet adds. A script processor now parses ContextInfo. The script starts a new key only when a line agrees with the key pattern. The script adds all other lines to the value of the last key. The parameter ignore_failure is set, thus a parse problem cannot stop the subsequent processors again. The behavior of the kv processor stays the same for the existing events: the keys keep their spaces, the separator can be "=" or ":", and the script removes the spaces from the start and the end of each value. The script also accepts \r\n line breaks, which the kv processor did not. The new test event comes from a Windows test VM, not from a user. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
a6f7d4a to
97ebb45
Compare
✅ Elastic Docs Style Checker (Vale)No issues found on modified lines! The Vale linter checks documentation changes against the Elastic Docs style guide. To use Vale locally or report issues, refer to Elastic style guide for Vale. |
…ntextinfo-painless Bump the windows package to 3.9.3 so the PowerShell 4103 changelog entry sits on top of versions already released on main.
🚀 Benchmarks reportPackage
|
| Data stream | Previous EPS | New EPS | Diff (%) | Result |
|---|---|---|---|---|
powershell_operational |
2314.81 | 1519.76 | -795.05 (-34.35%) | 💔 |
applocker_exe_and_dll |
4424.78 | 3389.83 | -1034.95 (-23.39%) | 💔 |
applocker_packaged_app_execution |
10101.01 | 3344.48 | -6756.53 (-66.89%) | 💔 |
forwarded |
1223.99 | 865.05 | -358.94 (-29.33%) | 💔 |
To see the full report comment with /test benchmark fullreport
…ntextinfo-painless
…peline Recognize ContextInfo keys only at eight-or-more spaces so indented script assignments are not treated as fields, record parse errors without aborting the pipeline, and add the multiline 4103 fixture to forwarded tests.
|
✅ All changelog entries have the correct PR link. |
💚 Build Succeeded
History
cc @marc-gr |
|
Pinging @elastic/security-service-integrations (Team:Security-Service Integrations) |
|
@nfritts - 💤 This PR was approved before I picked it up, so I will leave it — automatic reviews are skipped on approved PRs. If you would still like a review, request one with a |
There was a problem hiding this comment.
🟡 Changes recommended
The new script parser’s documentation is currently inaccurate about “parse failures,” and the key regex is overly permissive in a way that can create false keys under plausible indented inputs.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR updates the Windows integration’s PowerShell Operational ingest pipelines to robustly parse Event ID 4103 winlog.event_data.ContextInfo when values contain indented multi-line scripts, preventing pipeline aborts and ensuring downstream enrichment (including Fleet @custom) still runs.
Changes:
- Replace the
kvprocessor used for 4103ContextInfoparsing with a Painlessscriptprocessor in both thepowershell_operationalandforwardedpipelines. - Add new 4103 fixtures and expected outputs to validate indented multi-line
Host Applicationparsing in both pipeline test suites. - Bump package version to 3.9.4 and add a corresponding changelog entry.
File summaries
| File | Description |
|---|---|
| packages/windows/manifest.yml | Bumps the Windows integration version to 3.9.4. |
| packages/windows/data_stream/powershell_operational/elasticsearch/ingest_pipeline/default.yml | Replaces 4103 kv parsing with a script-based parser to handle indented multi-line values and avoid pipeline stoppage. |
| packages/windows/data_stream/forwarded/elasticsearch/ingest_pipeline/powershell_operational.yml | Mirrors the same script-based 4103 parsing change for forwarded events. |
| packages/windows/data_stream/powershell_operational/_dev/test/pipeline/test-events.json | Adds a 4103 test event with an indented multi-line script in Host Application. |
| packages/windows/data_stream/powershell_operational/_dev/test/pipeline/test-events.json-expected.json | Adds expected output validating full command line preservation and no parsing failure. |
| packages/windows/data_stream/forwarded/_dev/test/pipeline/test-powershell-operational-events.json | Adds the same 4103 test event for the forwarded pipeline tests. |
| packages/windows/data_stream/forwarded/_dev/test/pipeline/test-powershell-operational-events.json-expected.json | Adds expected output for forwarded pipeline parsing behavior. |
| packages/windows/changelog.yml | Adds release notes for 3.9.4 describing the 4103 parsing fix. |
Review details
Suppressed comments (2)
packages/windows/data_stream/powershell_operational/elasticsearch/ingest_pipeline/default.yml:19
- The key regex allows zero whitespace before the separator ("\s*[:=]"), so it will also treat lines like "Server=localhost" as a new key if they appear with >=8 leading spaces. Windows ContextInfo keys in the test fixtures use a space before ':' or '=', so requiring at least one whitespace here reduces false positives while matching observed input.
def keyPattern = /^\s{8,}([A-Za-z][A-Za-z0-9 _.\-]*?)\s*[:=](.*)$/;
packages/windows/data_stream/forwarded/elasticsearch/ingest_pipeline/powershell_operational.yml:19
- The key regex allows zero whitespace before the separator ("\s*[:=]"), so it will also treat lines like "Server=localhost" as a new key if they appear with >=8 leading spaces. Windows ContextInfo keys in the test fixtures use a space before ':' or '=', so requiring at least one whitespace here reduces false positives while matching observed input.
def keyPattern = /^\s{8,}([A-Za-z][A-Za-z0-9 _.\-]*?)\s*[:=](.*)$/;
- Files reviewed: 8/8 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| Parse failures are recorded on error.message without stopping the rest of the pipeline, | ||
| including the @custom pipeline that Fleet adds. |
| Parse failures are recorded on error.message without stopping the rest of the pipeline, | ||
| including the @custom pipeline that Fleet adds. |
|
Package windows - 3.9.4 containing this change is available at https://epr.elastic.co/package/windows/3.9.4/ |
The
logs-windows.powershell_operationalpipeline fails on the PowerShell events with the ID4103. Elasticsearch writes this message to
error.message:The document goes to the data stream, but the pipeline does not enrich it.
Why the kv processor fails
winlog.event_data.ContextInfois a block of indented<Key> = <Value>lines. One of the keysis
Host Application. Its value is the full command line of the PowerShell host process. If auser starts PowerShell with
powershell.exe -Command <script>, the full script goes into thatvalue. The script keeps its line breaks and its indents.
PR #16013 changed
field_splitfrom\nto\n(?!\n)\s+for this reason. That change is notcomplete. The pattern keeps a multi-line value together only if the subsequent lines start at
column 0, which is the format of the test data in PR #16013. But the lines of an embedded script
usually have an indent. Thus the
kvprocessor makes a new key from each of these lines. If sucha line contains no
:character and no=character, the processor fails.The
kvprocessor is also the first processor in the pipeline, and it has noignore_failureparameter. Therefore a failure stops the full pipeline, and all subsequent processors do not run.
This includes the
logs-windows.powershell_operational@custompipeline that Fleet adds. A userreports that a custom
dropprocessor does not run because of this failure.What this PR does
This PR replaces the
kvprocessor with ascriptprocessor. The same package already uses thismethod for the events 4xx and 600 in
data_stream/forwarded/elasticsearch/ingest_pipeline/powershell.yml.The script examines each line of
ContextInfo:The key pattern is
^\s{8,}([A-Za-z][A-Za-z0-9 _.\-]*?)\s*[:=](.*)$. A key must have at leasteight leading spaces, and a key must start with a letter. Therefore the script does not make a
key from a line such as
foreach ($i in $items) {,'alpha',,Server=localhost, or anindented assignment such as
int x = 1;.Parse failures append to
error.messageand do not stop later processors, including the@custompipeline that Fleet adds.The change keeps the behavior of the
kvprocessor:=or:. Localized events in the existing tests already use:.There is one difference: the script splits on
/\r?\n/and thus also accepts CRLF line breaks.The
kvprocessor hadtrim_value: " \n\t", which does not contain\r, and thus kept acarriage return at the end of each value on CRLF input. No test event uses CRLF, so no expected
file changes.
The script appends a line break with
(char)10, because a Painless string literal cannot contain"\n". This agrees with the other scripts in the repository: the entropy script in this same fileuses
int lf = 10;andaws_securityhubusesString.valueOf((char)10).The same change is made in the two identical copies of this pipeline:
data_stream/powershell_operational/elasticsearch/ingest_pipeline/default.ymldata_stream/forwarded/elasticsearch/ingest_pipeline/powershell_operational.ymlThe new 4103 fixture is in both
powershell_operationalandforwardedpipeline tests.Checklist
changelog.ymlfile.Author's Checklist
process.command_line(23 lines).How to test this PR locally
Alternatively, do these steps to see the problem and the correction:
Host Applicationvalue must containa script with more than one line. The script must contain a line that has an indent and no
:or
=character. The 11th event intest-events.jsonis such an event.POST _ingest/pipeline/logs-windows.powershell_operational-3.9.3/_simulate.The result contains
error.message.error.message, andprocess.command_linecontains the full script.Test results
Run against a live stack with
elastic-packagev0.126.0:powershell_operational: PASS, and the ingest pipeline warnings test also passes.forwarded: 200 PASS, 0 FAIL.process.command_linewith all 23 lines of the script, no leakedfields, and no
error.message.Related issues