Repository navigation
[ping_directory] Add PingDirectory integration. - #21267
Conversation
The initial release includes http_access data stream, associated dashboard and visualizations. PingDirectory fields are mapped to their corresponding ECS fields where possible. Test samples were derived from live data samples, which were subsequently sanitized. --------- Co-authored-by: muskan-crest <muskan.agarwal@crestdata.ai>
…ory. (elastic#19680) The initial release includes audit data stream, associated dashboard and visualizations. PingDirectory fields are mapped to their corresponding ECS fields where possible. Test samples were derived from live data samples, which were subsequently sanitized.
…m in Pingdirectory. (elastic#19678)
Elastic Docs Style Checker (Vale)Summary: 5 warnings, 10 suggestions found
|
| File | Line | Rule | Message |
|---|---|---|---|
| packages/ping_directory/_dev/build/docs/README.md | 163 | Elastic.DirectionalLanguage | Don't use directional language. Use 'the following element' instead of 'the table below'. |
| packages/ping_directory/data_stream/http_access/fields/fields.yml | 6 | Elastic.Latinisms | Latin terms and abbreviations are a common source of confusion. Use 'for example' instead of 'e.g'. |
| packages/ping_directory/data_stream/ldap_access/fields/fields.yml | 96 | Elastic.Latinisms | Latin terms and abbreviations are a common source of confusion. Use 'for example' instead of 'e.g'. |
| packages/ping_directory/data_stream/ldap_access/fields/fields.yml | 102 | Elastic.Latinisms | Latin terms and abbreviations are a common source of confusion. Use 'for example' instead of 'e.g'. |
| packages/ping_directory/data_stream/ldap_access/fields/fields.yml | 129 | Elastic.Latinisms | Latin terms and abbreviations are a common source of confusion. Use 'for example' instead of 'e.g'. |
💡 Suggestions (10): Optional style improvements. Apply when helpful.
| File | Line | Rule | Message |
|---|---|---|---|
| packages/ping_directory/_dev/build/docs/README.md | 20 | Elastic.Semicolons | Use semicolons sparingly. Consider splitting the sentence or using a comma or conjunction. |
| packages/ping_directory/_dev/build/docs/README.md | 20 | Elastic.Semicolons | Use semicolons sparingly. Consider splitting the sentence or using a comma or conjunction. |
| packages/ping_directory/changelog.yml | 1 | Elastic.Versions | Use 'later versions' instead of 'newer versions' when referring to versions. |
| packages/ping_directory/data_stream/audit/fields/fields.yml | 15 | Elastic.Ellipses | Use ellipses sparingly. Remove the ellipsis unless it appears in UI text. |
| packages/ping_directory/data_stream/audit/fields/fields.yml | 30 | Elastic.Ellipses | Use ellipses sparingly. Remove the ellipsis unless it appears in UI text. |
| packages/ping_directory/data_stream/audit/fields/fields.yml | 48 | Elastic.Ellipses | Use ellipses sparingly. Remove the ellipsis unless it appears in UI text. |
| packages/ping_directory/data_stream/audit/fields/fields.yml | 63 | Elastic.Ellipses | Use ellipses sparingly. Remove the ellipsis unless it appears in UI text. |
| packages/ping_directory/data_stream/audit/fields/fields.yml | 72 | Elastic.Ellipses | Use ellipses sparingly. Remove the ellipsis unless it appears in UI text. |
| packages/ping_directory/data_stream/audit/fields/fields.yml | 105 | Elastic.Ellipses | Use ellipses sparingly. Remove the ellipsis unless it appears in UI text. |
| packages/ping_directory/data_stream/ldap_access/fields/fields.yml | 15 | Elastic.WordChoice | Consider using 'can, might' instead of 'May', unless the term is in the UI. |
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.
|
Pinging @elastic/security-service-integrations (Team:Security-Service Integrations) |
|
Changelog link mismatch — expected
Tip If expected, add the |
🚀 Benchmarks reportTo see the full report comment with |
| source: > | ||
| int idx = ctx.event.original.indexOf('['); | ||
| if (idx > -1) { | ||
| ctx.event.original = ctx.event.original.substring(idx); |
There was a problem hiding this comment.
Severity: 🟡 Medium confidence: high path: packages/ping_directory/data_stream/ldap_access/elasticsearch/ingest_pipeline/default.yml:29
The trim_header script rewrites event.original to strip the syslog header; parse the body into a scratch field instead so event.original keeps the raw UDP message.
Details
The UDP template (agent/stream/udp.yml.hbs) applies no syslog processor, so messages arrive with their RFC 5424 header intact, as the syslog-prefixed line 7 of the handwritten fixture shows. trim_header then does ctx.event.original = ctx.event.original.substring(idx), permanently discarding the priority, timestamp, hostname, app name and procid. event.original is meant to be an untouched copy of the received message and no processor after the initial rename should mutate it; a user who enables preserve_original_event on the UDP stream still loses part of the original event, and the syslog hostname/appname are never available for mapping.
Recommendation:
Leave event.original intact and extract the body into a scratch field that the later drop/grok steps read from, for example:
- grok:
field: event.original
tag: split_syslog_header
patterns:
- '^<%{NONNEGINT:_tmp.syslog_pri}>%{NONNEGINT}? ?%{GREEDYDATA:_tmp.syslog_meta} (?<_tmp.body>\[%{GREEDYDATA})$'
- '^(?<_tmp.body>\[%{GREEDYDATA})$'
if: ctx.event?.original != null
ignore_failure: true
- grok:
field: _tmp.body
tag: extract_event_original
patterns:
- '^\[%{DATA:ping_directory.ldap_access.timestamp} %{ISO8601_TIMEZONE:ping_directory.ldap_access.timezone}\] %{DATA:ping_directory.ldap_access.operation_type} (?=[a-zA-Z][a-zA-Z0-9]*=)%{GREEDYDATA:ping_directory.ldap_access.kvpairs}$'
if: ctx._tmp?.body != nullThen point drop_non_timestamp_lines at _tmp.body and add _tmp to the final remove_fields_mapped_to_ecs list.
🤖 AI-Generated Review | Vera Review Bot - v0.4.1 | 📚 Knowledge base: integration-skills
⚠️ Automated review — verify suggestions before applying.
| field: event.original | ||
| tag: extract_event_original | ||
| patterns: | ||
| - '\[%{DATA:ping_directory.ldap_access.timestamp} %{ISO8601_TIMEZONE:ping_directory.ldap_access.timezone}\] %{DATA:ping_directory.ldap_access.operation_type} (?=[a-zA-Z][a-zA-Z0-9]*=)%{GREEDYDATA:ping_directory.ldap_access.kvpairs}' |
There was a problem hiding this comment.
Severity: 🟡 Medium confidence: high path: packages/ping_directory/data_stream/ldap_access/elasticsearch/ingest_pipeline/default.yml:41
Several grok patterns are not anchored with ^ and $; anchor them so the regex engine fails fast instead of scanning for a partial match.
Details
extract_event_original (line 41), extract_disconnect_msg (line 65) and parse_via_field (line 99) in the ldap_access pipeline have neither a leading ^ nor a trailing $. The http_access parse_http_access pattern (line 33) ends with \s*$ but has no leading ^, so it can still match starting mid-line. Unanchored grok lets the engine search the whole input for a substring match, which is slower on long lines (the DISCONNECT msg values in the fixture run to several hundred characters) and can silently accept lines with unexpected leading content.
Recommendation:
Add ^ and $ to each pattern, e.g. for the main ldap_access grok:
patterns:
- '^\[%{DATA:ping_directory.ldap_access.timestamp} %{ISO8601_TIMEZONE:ping_directory.ldap_access.timezone}\] %{DATA:ping_directory.ldap_access.operation_type} (?=[a-zA-Z][a-zA-Z0-9]*=)%{GREEDYDATA:ping_directory.ldap_access.kvpairs}$'and for extract_disconnect_msg:
patterns:
- '^%{GREEDYDATA:ping_directory.ldap_access.kv_prefix} msg="%{GREEDYDATA:ping_directory.ldap_access.msg}"$'Apply the same to parse_via_field and prefix the http_access pattern with ^.
Also in: packages/ping_directory/data_stream/ldap_access/elasticsearch/ingest_pipeline/default.yml:65, packages/ping_directory/data_stream/ldap_access/elasticsearch/ingest_pipeline/default.yml:99, packages/ping_directory/data_stream/http_access/elasticsearch/ingest_pipeline/default.yml:33
🤖 AI-Generated Review | Vera Review Bot - v0.4.1 | 📚 Knowledge base: integration-skills
⚠️ Automated review — verify suggestions before applying.
There was a problem hiding this comment.
The grok needs no change , as tested on the current implementation and approved.
| - name: streamed_entries_from_index | ||
| type: keyword | ||
| description: Number of entries streamed directly from an index during search processing. | ||
| - name: thread_id |
There was a problem hiding this comment.
Severity: 🔵 Low confidence: medium path: packages/ping_directory/data_stream/ldap_access/fields/fields.yml:121
Numeric LDAP access values are mapped as keyword (thread_id, streamed_entries_from_index) or left as strings for a numeric type (scope); align them with the long typing used by the audit and http_access streams.
Details
ping_directory.ldap_access.thread_id is declared keyword (line 122), and the pipeline first converts it to long (convert_threadID_to_long) and then back to string (convert_thread_id_to_keyword) to satisfy that mapping. The same vendor value is mapped as long in data_stream/audit/fields/fields.yml (line 112) and data_stream/http_access/fields/fields.yml (line 67), so the three streams cannot be compared or aggregated on it consistently. streamed_entries_from_index (line 118) is described as a number of entries but mapped keyword, so it cannot be summed or averaged. scope (line 106) is mapped byte but no convert runs on it, so the source holds the string "2" and only Elasticsearch coercion makes the mapping work; every other numeric field in this pipeline is explicitly converted. None of this breaks the build, so it is a consistency/maintainability issue rather than a defect.
Recommendation:
Map thread_id and streamed_entries_from_index as long, drop the convert_thread_id_to_keyword processor, and add a convert with type: integer (and ignore_missing: true) for scope alongside the other numeric conversions.
- name: scope
type: byte
description: 'LDAP search scope: 0=baseObject, 1=singleLevel, 2=wholeSubtree.'
- name: streamed_entries_from_index
type: long
description: Number of entries streamed directly from an index during search processing.
- name: thread_id
type: long
description: ID of the server thread that handled the LDAP operation.Also in: packages/ping_directory/data_stream/ldap_access/fields/fields.yml:118, packages/ping_directory/data_stream/ldap_access/fields/fields.yml:106
🤖 AI-Generated Review | Vera Review Bot - v0.4.1 | 📚 Knowledge base: integration-skills
⚠️ Automated review — verify suggestions before applying.
There was a problem hiding this comment.
Id fields should be mapped as keyword.
| @@ -0,0 +1,5 @@ | |||
| {"displayName":"John Doe","emails":["john.doe@example.com","jonny.doe@example.com"],"name":{"familyName":"Doe","formatted":"John Doe","givenName":"John"},"title":"DevOps Engineer","userName":"john.doe","userType":"Full-Time","id":"c9bbce6c-7d77-4d93-b674-42b1e8a00606","meta":{"resourceType":"User","location":"https://10.50.15.29:2443/scim/v2/Users/c9bbce6c-7d77-4d93-b674-42b1e8a00606"},"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"]} | |||
There was a problem hiding this comment.
Severity: 🔵 Low confidence: medium path: packages/ping_directory/data_stream/user/_dev/test/pipeline/test-user.log:1
user and group fixtures contain no CEL error event, so the terminate branch for collection errors is untested; add an error-shaped JSON fixture.
Details
Both CEL programs emit {"error": {"code": ..., "id": ..., "message": ...}} events on authentication and HTTP failures, and both pipelines start with a terminate processor keyed on ctx.error?.message != null with no message. Neither NDJSON fixture includes such a record, so the failure path that indexes the error document is not covered by pipeline tests. Because the fixtures are .log files each line becomes message, so covering this branch needs a .json events fixture where error.message is a top-level field.
Recommendation:
Add a test-user-error.json (and test-group-error.json) fixture using the {"events": [...]} shape with a top-level error object and no message, for example:
{
"events": [
{
"error": {
"code": "401",
"id": "401 Unauthorized",
"message": "GET https://example.com/scim/v2/Users: re-authentication did not clear 401"
}
}
]
}Then regenerate the expected output and confirm the document is terminated with error.message preserved.
Also in: packages/ping_directory/data_stream/group/_dev/test/pipeline/test-group.log:1
🤖 AI-Generated Review | Vera Review Bot - v0.4.1 | 📚 Knowledge base: integration-skills
⚠️ Automated review — verify suggestions before applying.
There was a problem hiding this comment.
We do not have a practice of adding such test log.
kcreddy
left a comment
There was a problem hiding this comment.
@muskan-agarwal26 there seems to be several differences found between feature branch feature/pingdirectory-0.1.0 and this PR.
Can you please check if this PR reflects the latest as per feature branch?
┌─────────────────────────────────────────────────────────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Category │ Change │
├─────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ manifest.yml │ format_version bumped 3.3.2 → 3.4.2; release: beta moved from default to agentless (correct placement) │
├─────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ audit, http_access, ldap_access pipelines │ ECS version bumped 9.4.0 → 9.5.0 │
├─────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ ldap_access pipeline │ Added if: ctx.source?.ip != null guard on two geoip processors (guards against null IP crashing); added event.kind: pipeline_error set on error path │
├─────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ group + user pipelines │ Added entity.* field population (entity.id, entity.name, entity.source, entity.last_seen_timestamp, entity.type) — new entity analytics enrichment │
├─────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ group/fields/ecs.yml, user/fields/ecs.yml, transform/latest_user/fields/ecs.yml │ entity.* ECS field declarations added │
├─────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ changelog.yml │ Initial release entry added (as expected) │
├─────────────────────────────────────────────────────────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Test fixtures + sample events │ Updated to reflect all the above │
└─────────────────────────────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Thanks for flagging this — I compared this PR against feature/pingdirectory-0.1.0. The feature branch is at the user/group merge. This PR is created from feature branch because edits can't be made in the feature branch, this PR additonally includes the Vera review fixes (ECS 9.5.0, entity.* on user/group, ldap_access geoip/pipeline_error guards, format_version 3.4.2 with release: beta under agentless, changelog initial-release for #21267, and updated fixtures). Those diffs are expected; this PR is the latest. |
| tag: parsing_kvpairs | ||
| target_field: ping_directory.ldap_access | ||
| if: ctx.ping_directory?.ldap_access?.operation_type != 'DISCONNECT' && (ctx.ping_directory?.ldap_access?.operation_type != 'SEARCH RESULT' || ctx.ping_directory?.ldap_access?.via == null) | ||
| field_split: ' (?=[A-Za-z][A-Za-z0-9]*=)' |
There was a problem hiding this comment.
Severity: 🟡 Medium confidence: high path: packages/ping_directory/data_stream/ldap_access/elasticsearch/ingest_pipeline/default.yml:90
The ldap_access kv processors split on any key= even inside a double-quoted value, so quoted values containing key=value text are torn apart; make the field_split quote-aware.
Details
field_split: ' (?=[A-Za-z][A-Za-z0-9]*=)' only looks for a space followed by an identifier and =; it does not know whether it is inside a quoted value. The fixture demonstrates the effect: line 5 of _dev/test/pipeline/test-ldap-access.log carries additionalInfo="clientName='config-tool' useDedicatedThreadPool=true", which this split turns into additional_info = clientName='config-tool' (trailing quote lost) plus a spurious top-level key useDedicatedThreadPool. The pipeline then codifies the misparse: convert_use_dedicated_thread_pool_to_boolean (line 615) and the use_dedicated_thread_pool: boolean declaration in fields.yml (line 124) exist only because of it, while additional_info is stored truncated. The same root cause is why via="app='...' clientIP='...' ..." needed the extract_via_field / kvpairs_without_via special case (lines 48-59, 105-113). Any other quoted value that contains <word>= after a space (for example message, additionalInfo, requestType) will be corrupted the same way in production, producing undeclared fields and truncated values. This is distinct from the grok-anchoring thread the maintainer closed on line 41; that thread did not cover the kv split.
Recommendation:
Split only on spaces that are followed by an even number of double quotes (i.e. outside a quoted value), and drop the via-specific pre-splitting plus the use_dedicated_thread_pool field, since additionalInfo then stays intact as one keyword value:
- kv:
field: ping_directory.ldap_access.kvpairs
tag: parsing_kvpairs
target_field: ping_directory.ldap_access
field_split: ' (?=(?:[^"]*"[^"]*")*[^"]*$)(?=[A-Za-z][A-Za-z0-9]*=)'
value_split: '='
trim_value: '"'
ignore_missing: trueApply the same field_split to parsing_kv_prefix, parsing_kvpairs_disconnect_no_msg and parsing_kvpairs_kvpairs_without_via (or remove the latter once via no longer needs isolating and parse via directly from the kv output with parse_via_field). Regenerate the expected output afterwards and confirm additional_info is the full string and no use_dedicated_thread_pool key appears.
Also in: packages/ping_directory/data_stream/ldap_access/elasticsearch/ingest_pipeline/default.yml:72, packages/ping_directory/data_stream/ldap_access/elasticsearch/ingest_pipeline/default.yml:81, packages/ping_directory/data_stream/ldap_access/elasticsearch/ingest_pipeline/default.yml:110
🤖 AI-Generated Review | Vera Review Bot - v0.4.1 | 📚 Knowledge base: integration-skills
⚠️ Automated review — verify suggestions before applying.
There was a problem hiding this comment.
Not required, the current one is tested and working as expected.
| - set: | ||
| field: user.id | ||
| tag: set_user_id_from_ldap_access_auth_dn | ||
| copy_from: ping_directory.ldap_access.auth_dn |
There was a problem hiding this comment.
Severity: 🟡 Medium confidence: medium path: packages/ping_directory/data_stream/ldap_access/elasticsearch/ingest_pipeline/default.yml:233
BIND RESULT records carry the bound identity in dn, but user.id only falls back to auth_dn, so authentication events (including failed binds) end up with no user.* fields; add a dn fallback for BIND RESULT.
Details
set_user_id_from_ldap_access_requester_dn reads requester_dn and set_user_id_from_ldap_access_auth_dn reads auth_dn. PingDirectory BIND RESULT lines identify the account being authenticated in dn= and only add authDN= after a successful bind; they carry no requesterDN. Fixture line 12 (BIND RESULT ... dn="cn=Directory Manager,cn=Root DNs,cn=config" authType="SIMPLE") has neither requesterDN nor authDN, so the pipeline categorizes the event as authentication (lines 186-191) yet sets no user.id, user.name or related.user. Failed binds, which never have authDN, are the most security-relevant case and are always affected. ping_directory.ldap_access.dn keeps the value, but the ECS user fields that detection rules key on stay empty.
Recommendation:
Add a third fallback that uses the bind target on BIND RESULT records, before the grok that derives user.name:
- set:
field: user.id
tag: set_user_id_from_ldap_access_bind_dn
copy_from: ping_directory.ldap_access.dn
ignore_empty_value: true
override: false
if: ctx.ping_directory?.ldap_access?.operation_type == 'BIND RESULT'
description: On Bind results the authenticated account is reported in `dn`; `authDN` is only present after a successful bind.Regenerate the expected output and confirm the BIND RESULT documents gain user.id, user.name and related.user.
🤖 AI-Generated Review | Vera Review Bot - v0.4.1 | 📚 Knowledge base: integration-skills
⚠️ Automated review — verify suggestions before applying.
| division: engineering | ||
| team: security-service-integrations | ||
| inputs: | ||
| - type: filestream |
There was a problem hiding this comment.
Severity: 🟡 Medium confidence: high path: packages/ping_directory/manifest.yml:58
The filestream and udp inputs are offered under the agentless deployment mode, where they cannot run (no local files, no inbound listener); restrict them to the default mode with deployment_modes: [default] on each input.
Details
The single policy template enables deployment_modes.agentless (lines 51-56) and lists three inputs: filestream, udp and cel. Only the CEL input (user and group SCIM collection) can work in agentless mode. The package-spec integration manifest schema defines policy_templates[].inputs[].deployment_modes (an array of default / agentless) exactly for this case, and it is available at the declared format_version: 3.4.2 (no version patch removes it). Without it, Fleet shows the log-file and UDP streams to agentless users, who cannot use them.
Recommendation:
Restrict the two host-bound inputs to the default deployment mode:
- type: filestream
title: Collect PingDirectory logs via Filestream
description: Collecting logs from PingDirectory via File.
deployment_modes:
- default
- type: udp
title: Collect PingDirectory logs via UDP
description: Collecting logs from PingDirectory via UDP.
deployment_modes:
- defaultAlso in: packages/ping_directory/manifest.yml:61
🤖 AI-Generated Review | Vera Review Bot - v0.4.1 | 📚 Knowledge base: integration-skills
⚠️ Automated review — verify suggestions before applying.
There was a problem hiding this comment.
We don't follow this practice.
| external: ecs | ||
| - name: entity.name | ||
| external: ecs | ||
| - name: entity.source |
There was a problem hiding this comment.
Severity: 🟡 Medium confidence: medium path: packages/ping_directory/data_stream/group/fields/ecs.yml:7
The group stream populates entity.id/name/source/last_seen_timestamp but never sets or declares entity.type, unlike the user stream; either set a valid entity.type or do not emit partial entity.* on group records.
Details
group/fields/ecs.yml declares entity.id, entity.last_seen_timestamp, entity.name and entity.source, and group/elasticsearch/ingest_pipeline/default.yml sets exactly those four (lines 167-185) with event.kind: asset. The user stream additionally appends entity.type (user, user pipeline line 259) and declares it in user/fields/ecs.yml. The maintainer's own summary of the delta lists entity.type among the fields added to both the group and user pipelines, but the group pipeline does not set it. The entity field catalog treats entity.type as a required core identity field for asset documents. Note that the ECS entity.type allowed-values list has no group value, so the fix is either to choose a value that exists in the pinned ECS schema or to drop the entity.* enrichment from the group stream rather than inventing one.
Recommendation:
If group records are meant to be entity-store assets, set and declare entity.type in the group stream the same way the user stream does, using a value that exists in ECS 9.5.0 (verify against the pinned schema before choosing). If groups are not intended as entities, remove the four entity.* set processors and the matching declarations so the stream does not emit a partial entity record.
# group/fields/ecs.yml
- name: entity.type
external: ecs# group/elasticsearch/ingest_pipeline/default.yml, next to set_entity_source
- append:
field: entity.type
tag: append_entity_type
value: <valid-ecs-entity-type>
allow_duplicates: false🤖 AI-Generated Review | Vera Review Bot - v0.4.1 | 📚 Knowledge base: integration-skills
⚠️ Automated review — verify suggestions before applying.
There was a problem hiding this comment.
entity,type now suitable for groups.
| @@ -0,0 +1,3 @@ | |||
| <svg xmlns="http://www.w3.org/2000/svg" width="124" height="163" viewBox="0 0 124 163"> | |||
| <image href="data:image/png;base64,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" width="124" height="163" /> | |||
There was a problem hiding this comment.
Severity: 🔵 Low confidence: high path: packages/ping_directory/img/ping_directory-logo.svg:2
The package icon is a raster PNG wrapped in an SVG element, not a vector image; provide a true vector SVG so the icon scales cleanly at the declared 32x32 size.
Details
img/ping_directory-logo.svg is a 124x163 <svg> whose only content is an <image> with a base64-encoded PNG. The root manifest declares this icon as type: image/svg+xml with size: 32x32. Fleet renders the icon at several sizes; a bitmap embedded in SVG blurs when scaled and the declared dimensions do not match the file's viewBox.
Recommendation:
Replace the file with a genuine vector export of the PingDirectory logo (paths, not an embedded PNG) and keep the manifest icons entry pointing at it. If only a raster asset is available from the vendor, the vendor's official SVG is preferred over a wrapped PNG.
🤖 AI-Generated Review | Vera Review Bot - v0.4.1 | 📚 Knowledge base: integration-skills
⚠️ Automated review — verify suggestions before applying.
There was a problem hiding this comment.
The file works good, change not required.
|
🟢 No issues across the latest commits b9418af.
|
💚 Build Succeeded
History
|
|
Tick the box to add this pull request to the merge queue (same as
|
|
Package ping_directory - 0.1.0 containing this change is available at https://epr.elastic.co/package/ping_directory/0.1.0/ |
Proposed commit message
The integration comprises of 5 datastream which are merged:
Checklist
changelog.ymlfile.How to test this PR locally
Related issues