-
Notifications
You must be signed in to change notification settings - Fork 519
[Neon Cyber] New Elastic integration neon_cyber for the Neon Cyber platform #15725
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
💚 CLA has been signed |
|
Pinging @elastic/security-service-integrations (Team:Security-Service Integrations) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need to add this file, it will be automatically generated.
| @@ -0,0 +1,45 @@ | |||
| {{- generatedHeader }} | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was in the template and adds the auto generation "Do not edit" in the rendered README.md
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't need it.
packages/neon_cyber/data_stream/events/elasticsearch/ingest_pipeline/default.yml
Outdated
Show resolved
Hide resolved
packages/neon_cyber/data_stream/events/elasticsearch/ingest_pipeline/default.yml
Outdated
Show resolved
Hide resolved
packages/neon_cyber/data_stream/detections/elasticsearch/ingest_pipeline/default.yml
Show resolved
Hide resolved
efd6
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.github/CODEOWNERS needs to be updated to include this package with the owner matching the owner in the package's manifest.
update dep ref version Co-authored-by: Dan Kortschak <dan.kortschak@elastic.co>
Co-authored-by: Dan Kortschak <dan.kortschak@elastic.co>
packages/neon_cyber/data_stream/detections/elasticsearch/ingest_pipeline/default.yml
Show resolved
Hide resolved
| - date: | ||
| field: json.detection_timestamp | ||
| target_field: neon_cyber.detections.detection_timestamp | ||
| tag: date_detection_timestamp | ||
| formats: | ||
| - ISO8601 | ||
| - date: | ||
| field: json.detection_timestamp | ||
| target_field: '@timestamp' | ||
| tag: date_timestamp | ||
| formats: | ||
| - ISO8601 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For these we might want to have on_failure handlers? How much can we tolerate the timestamps being absent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added on_failure just in case but these are guaranteed to be there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I should be clearer (your interpretation is entirely reasonable, but not what I meant).
Let's say there is an invalid timestamp so the parse fails, is the document now useless (because it will not have the timestamp) or is it something that we can salvage some information from? If it's the former, we don't need to have an on_failure handler since we can just say definitively that the document had an error and we didn't try to do any further work, but if it's the latter, we can say, "there was a problem with this field, but I kept going and be aware of the error when you look at the document".
It's no doubt unlikely that there would be a corrupted timestamp, but we live in the real world, so it is always possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The latter would be correct. Especially for detections we wouldn't want to ignore any information. In that case is the on_failure I added correct, or is there another suggestion?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, if that's the semantics, what you've done is right.
I would say though that the json processor failure would be unrecoverable; if that fails there's no point in doing any further work since we can guarantee that the fields will be absent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, committed a change to remove json on_failure
packages/neon_cyber/manifest.yml
Outdated
| - name: enable_request_tracer | ||
| type: bool | ||
| title: Enable request tracing | ||
| default: false | ||
| multi: false | ||
| required: false | ||
| show_user: false | ||
| description: The request tracer logs requests and responses to the agent's local file-system for debugging configurations. Enabling this request tracing compromises security and should only be used for debugging. See [documentation](https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-input-cel.html#_resource_tracer_filename) for details. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be at the data stream level.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved to DS
| i.e. scheme://host:port/path | ||
| required: true | ||
| show_user: true | ||
| default: https://api.neoncyber.io/v1/detections |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the sake of user ergonomics, is it the case that the detections endpoint is always at /v1/detections? (similarly for events at /v1/events). If it is, I would suggest hard coding the endpoint path into the CEL program and making the base URL a package level configuration next to the API token field. This would mean that that user only needs to configure it once (assuming that the base URL is almost constant for a user).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved URL to package and hardcoded and removed resource_url
| ), | ||
| }, | ||
| }, | ||
| "want_more": false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not necessary.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any time I remove this "want_more" line "elastic_package test system" fails to get a hit on the mock server and I don't know why.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. I will take a look and see what is going on.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your help! I couldn't figure out how that works since on paper it seem extraneous but the system test definitely prevents it from completing if that single line is removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue is identified in the error message that unfortunately only gets shown in the logs
{
"log.level": "error",
"@timestamp": "2025-11-05T22:21:36.369Z",
"log.origin": {
"function": "github.com/elastic/elastic-agent/internal/pkg/agent/application/coordinator.logComponentStateChange",
"file.name": "coordinator/coordinator.go",
"file.line": 800
},
"message": "Unit state changed cel-default-cel-neon_cyber-50f5cedf-f9ea-481d-af64-5a5da5a63c74 (STARTING->FAILED): failed to check program: failed compilation: ERROR: <input>:12:54: found no matching overload for '_?_:_' applied to '(bool, map(string, list(map(string, string))), map(string, map(string, map(string, string))))'\n | ).do_request().as(resp, (resp.StatusCode == 200) ?\n | .....................................................^ accessing config",
"log": {
"source": "elastic-agent"
},
"component": {
"id": "cel-default",
"state": "HEALTHY"
},
"unit": {
"id": "cel-default-cel-neon_cyber-50f5cedf-f9ea-481d-af64-5a5da5a63c74",
"type": "input",
"state": "FAILED",
"old_state": "STARTING"
},
"ecs.version": "1.6.0"
}
extracting the relevant part
found no matching overload for '_?_:_' applied to '(bool, map(string, list(map(string, string))), map(string, map(string, map(string, string))))'
| ).do_request().as(resp, (resp.StatusCode == 200) ?
| .....................................................^ accessing config
What this is saying is that the ternary has branches that are not the same type, map(string, list(map(string, string))) and map(string, map(string, map(string, string))). They need to agree, and we can achieve this by making them both dyn types so that the compile-time types are the same even though the runtime types are not identical.
This is done by (with some other cosmetic changes):
diff --git a/packages/neon_cyber/data_stream/detections/agent/stream/cel.yml.hbs b/packages/neon_cyber/data_stream/detections/agent/stream/cel.yml.hbs
index ea6c2524d4..efa226430e 100644
--- a/packages/neon_cyber/data_stream/detections/agent/stream/cel.yml.hbs
+++ b/packages/neon_cyber/data_stream/detections/agent/stream/cel.yml.hbs
@@ -34,31 +34,34 @@ program: |-
).do_request().as(resp, (resp.StatusCode == 200) ?
resp.Body.decode_json().as(body,
{
- "events": (has(body.data) && body.data.size() > 0) ?
- body.data.map(e,
- {
- "message": e.encode_json(),
- }
- )
- :
- []
+ "events": dyn(
+ (has(body.data) && body.data.size() > 0) ?
+ body.data.map(e,
+ {
+ "message": e.encode_json(),
+ }
+ )
+ :
+ []
+ )
}
)
:
{
- "events": {
- "error": {
- "code": string(resp.StatusCode),
- "id": string(resp.Status),
- "message": "GET " + state.url + (
- (size(resp.Body) != 0) ?
- string(resp.Body)
- :
- string(resp.Status) + " (" + string(resp.StatusCode) + ")"
- ),
- },
- },
- "want_more": false,
+ "events": dyn(
+ {
+ "error": {
+ "code": string(resp.StatusCode),
+ "id": string(resp.Status),
+ "message": "GET " + state.url.trim_right("/") + "/v1/detections: " + (
+ (size(resp.Body) != 0) ?
+ string(resp.Body)
+ :
+ string(resp.Status) + " (" + string(resp.StatusCode) + ")"
+ ),
+ },
+ }
+ )
}
)
)
diff --git a/packages/neon_cyber/data_stream/events/agent/stream/cel.yml.hbs b/packages/neon_cyber/data_stream/events/agent/stream/cel.yml.hbs
index 40341dcf6b..54cb70b0ac 100644
--- a/packages/neon_cyber/data_stream/events/agent/stream/cel.yml.hbs
+++ b/packages/neon_cyber/data_stream/events/agent/stream/cel.yml.hbs
@@ -34,31 +34,34 @@ program: |-
).do_request().as(resp, (resp.StatusCode == 200) ?
resp.Body.decode_json().as(body,
{
- "events": (has(body.data) && body.data.size() > 0) ?
- body.data.map(e,
- {
- "message": e.encode_json(),
- }
- )
- :
- []
+ "events": dyn(
+ (has(body.data) && body.data.size() > 0) ?
+ body.data.map(e,
+ {
+ "message": e.encode_json(),
+ }
+ )
+ :
+ []
+ )
}
)
:
{
- "events": {
- "error": {
- "code": string(resp.StatusCode),
- "id": string(resp.Status),
- "message": "GET " + state.url + (
- (size(resp.Body) != 0) ?
- string(resp.Body)
- :
- string(resp.Status) + " (" + string(resp.StatusCode) + ")"
- ),
- },
- },
- "want_more": false,
+ "events": dyn(
+ {
+ "error": {
+ "code": string(resp.StatusCode),
+ "id": string(resp.Status),
+ "message": "GET " + state.url.trim_right("/") + "/v1/events: " + (
+ (size(resp.Body) != 0) ?
+ string(resp.Body)
+ :
+ string(resp.Status) + " (" + string(resp.StatusCode) + ")"
+ ),
+ },
+ }
+ )
}
)
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Updated to your suggestion and system tests are passing now.
…t_pipeline/default.yml Co-authored-by: Dan Kortschak <dan.kortschak@elastic.co>
efd6
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor changes only, otherwise looking good.
| service: neon_cyber | ||
| vars: | ||
| api_token: xxxx | ||
| enable_request_tracer: true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be moved to match the new manifest layout. Same in the other data stream.
| enable_request_tracer: true | ||
| data_stream: | ||
| vars: | ||
| resource_url: http://{{Hostname}}:{{Port}}/v1/detections |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will need to be moved to the package level and changed to url (at the moment the test is reaching out to api.neoncyber.io and failing (correctly) with a 401).
{
"log.level": "debug",
"@timestamp": "2025-11-05T22:40:13.138Z",
"message": "HTTP request",
"transaction.id": "S6QK57BN7LQHG-1",
"url.original": "https://api.neoncyber.io/v1/detections",
"url.scheme": "https",
"url.path": "/v1/detections",
"url.domain": "api.neoncyber.io",
"url.port": "",
"url.query": "",
"http.request.method": "GET",
"http.request.header": {
"Authorization": [
"xxxx"
],
"User-Agent": [
"Elastic-Filebeat/8.19.3 (linux; amd64; 7a508104ea6b6ddae7456dff8b61423e59a73962; 2025-08-26 01:12:39 +0000 UTC)"
]
},
"user_agent.original": "Elastic-Filebeat/8.19.3 (linux; amd64; 7a508104ea6b6ddae7456dff8b61423e59a73962; 2025-08-26 01:12:39 +0000 UTC)",
"http.request.body.content": "",
"http.request.body.truncated": false,
"http.request.body.bytes": 0,
"http.request.mime_type": "",
"ecs.version": "1.6.0"
}
{
"log.level": "debug",
"@timestamp": "2025-11-05T22:40:14.281Z",
"message": "HTTP response",
"transaction.id": "S6QK57BN7LQHG-1",
"http.response.status_code": 401,
"http.response.body.content": "{\"error\":{\"code\":401,\"message\":\"Unauthorized\"},\"data\":null}",
"http.response.body.truncated": false,
"http.response.body.bytes": 59,
"http.response.mime_type": "application/json; charset=utf-8",
"http.response.header": {
"Apigw-Requestid": [
"Tl3MPgXKIAMEJbw="
],
"Content-Length": [
"59"
],
"Content-Type": [
"application/json; charset=utf-8"
],
"Date": [
"Wed, 05 Nov 2025 22:40:14 GMT"
],
"Etag": [
"W/\"3b-B6EKjFN6TdKYf7gDSWml69abDvE\""
],
"Server": [
"envoy"
],
"X-Envoy-Upstream-Service-Time": [
"159"
]
},
"ecs.version": "1.6.0"
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. My bad.
|
/test |
|
/test |
efd6
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You will need to run
elastic-package test system -g
elastic-package test pipeline -g
elastic-package test policy -g
elastic-package build
using a v8.18.0 stack (the version that is the minimum specified in the manifest)
| field: json.url | ||
| target_field: neon_cyber.events.url | ||
| ignore_missing: true | ||
| tag: rename_events_url |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- uri_parts:
field: neon_cyber.events.url
ignore_missing: true
tag: uri_parts_events_url
| field: json.url | ||
| target_field: neon_cyber.detections.url | ||
| ignore_missing: true | ||
| tag: rename_detection_url |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- uri_parts:
field: neon_cyber.detections.url
ignore_missing: true
tag: uri_parts_detections_url
|
Switched manifest version to match testing config version 8.17.0. Regenerated expected and samples |
|
/test |
🚀 Benchmarks reportTo see the full report comment with |
💚 Build Succeeded
History
|
|
Quick query: where were the test sample obtained from? Live API with modification? Constructed from documentation? |
Live API for a test account and modifications |
efd6
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks
|
Package neon_cyber - 0.1.0 containing this change is available at https://epr.elastic.co/package/neon_cyber/0.1.0/ |
…atform (elastic#15725) The initial release includes detections and events data streams. Neon Cyber fields are mapped to their corresponding ECS fields where possible. Test samples were derived from a live API using a test account.
Proposed commit message
Changes made
Checklist
changelog.ymlfile.Author's Checklist
How to test this PR locally
Related issues
Screenshots