Skip to content

RFPD-96030: add rf-alert-lookup command#12

Merged
aommm merged 1 commit into
pack_v3_rf_alert_images_command_error_handlingfrom
RFPD-96030-rf-alert-lookup
Mar 18, 2026
Merged

RFPD-96030: add rf-alert-lookup command#12
aommm merged 1 commit into
pack_v3_rf_alert_images_command_error_handlingfrom
RFPD-96030-rf-alert-lookup

Conversation

@aommm

@aommm aommm commented Mar 10, 2026

Copy link
Copy Markdown
Collaborator

Status

  • In Progress
  • Ready
  • In Hold - (Reason for hold)

Related Issues

Description

Add rf-alert-lookup command

Must have

  • Tests
  • Documentation

@aommm aommm force-pushed the RFPD-96030-rf-alert-lookup branch from 0382e72 to d395c91 Compare March 16, 2026 13:46
@aommm aommm force-pushed the RFPD-96030-rf-alert-lookup branch from d395c91 to 79f2dc4 Compare March 17, 2026 14:39
@aommm aommm changed the base branch from master to pack_v3_rf_alert_images_command_error_handling March 18, 2026 12:05
@aommm aommm marked this pull request as ready for review March 18, 2026 12:05
@aommm aommm merged commit d395cb2 into pack_v3_rf_alert_images_command_error_handling Mar 18, 2026
21 checks passed
hildenjohannes pushed a commit that referenced this pull request Jun 22, 2026
…and resource filters (demisto#44217)

* Wiz content pack v2.0.3: bidirectional mirroring, null-safety fixes, and resource filters (demisto#43959)

* Truncate issue notes to 1400 chars to prevent API errors (WZ-95550)

The Wiz API rejects notes exceeding 1400 characters, causing a 4.41%
error rate on createIssueNote. Add truncate_note() to both Wiz and
WizDefend integrations, applied in set_issue_comment/set_issue_note
and reject_or_resolve_issue.

* Remove unsupported resolution reasons from XSOAR commands (WZ-100065)

Remove system-assigned resolution reasons (DETECTION_EXPIRED,
CONTROL_DISABLED, CONTROL_DELETED) that the updateIssue mutation
does not accept from wiz-reject-issue and wiz-resolve-issue commands.
Also remove OBJECT_DELETED and ISSUE_FIXED from wiz-reject-issue
since they are only valid for resolve, not reject.

Update WizDefend README to match its YAML definition.
Bump integration versions (Wiz 1.5.0, WizDefend 1.1.0) and
pack version to 2.0.2.

* Remove deprecated runtimeExecutionDataId from evidence query (WZ-83470)

The runtimeExecutionDataId field was removed from the CloudEventRuntimeDetails
GraphQL type, causing wiz-get-issue-evidence to always fail with:
"Cannot query field runtimeExecutionDataId on type CloudEventRuntimeDetails"

* Add project_ids, native_types, and updatedAt filters to wiz-get-resources (WZ-51676)

* Fix test_has_next_page missing getLastRun mock

The test was failing because demisto.getLastRun() returned {} causing
fetch_issues to call dateparser.parse which isn't available locally.

* Add bidirectional mirroring to Wiz XSOAR integration (WZ-64007)

Add incoming (Wiz → XSOAR) and outgoing (XSOAR → Wiz) mirroring
support so XSOAR incidents stay in sync with Wiz issues.

- Add mirror_direction, mirror_limit, comment_tag config params
- Implement get-remote-data, get-modified-remote-data,
  update-remote-system, and get-mapping-fields commands
- Add incoming mapper fields (dbotMirror*) and outgoing mapper
- Use statusChangedAt filter for change detection (updatedAt
  not available in Wiz API)
- Prevent mirror loops via XSOAR_MIRROR_MARKER in note text
- Default mirror direction is None (preserves existing behavior)

* Address review findings: update release notes, README, and mirror direction validation

* Fix clearing due date not propagating to Wiz via mirror

* Fix null detection descriptions and fetch window overlap in WizDefend (WZ-102115, WZ-103753)

WZ-102115: Add build_fallback_description() to generate descriptions from
severity, rule name, and detection ID when the API returns null. Sanitize
null descriptions in get_filtered_detections() and map description to the
XSOAR incident details field in build_incidents().

WZ-103753: Fix _save_pagination_context() saving BEFORE = stored_after
instead of stored_before, which corrupted pagination windows. Change
get_api_before_parameter() to use a lagged boundary (now - fetch_interval)
for fresh runs instead of raw now, preventing fetch window overlap.

* Extract _fetch_all_issue_nodes helper and fix missing pagination in get_modified_remote_data

get_modified_remote_data_command was not paginating, silently dropping
issues beyond the first page. Extracted the repeated pagination loop
into a shared helper and applied it to all three callers.

* Fix outgoing mirror status sync silently failing for resolve/reject

Mirror was calling resolve_issue/reject_issue which (a) passed empty
note strings that failed the falsy check in reject_or_resolve_issue,
and (b) hit the THREAT_DETECTION type restriction in resolve_issue.
Both failures returned error strings swallowed by try/except.

Now calls reject_or_resolve_issue directly with a default note,
bypassing the type restriction meant for the XSOAR command.

* Fix incident close ignoring resolution reason and improve mirror config clarity

- Pass delta resolutionReason to _handle_incident_closed instead of
  always using WONT_FIX default
- Elevate _handle_incident_closed error log from debug to info
- Clarify mirror_limit param is a page size, not a total cap
- Prevent _fetch_all_issue_nodes from mutating caller's variables dict

* Add unit tests for 11 coverage gaps in Wiz and WizDefend mirror/pagination/filter code

Fill gaps identified in v2.0.2 test plan:
- _fetch_all_issue_nodes multi-page cursor propagation and node concatenation
- _mirror_status_to_wiz for in_progress and rejected statuses
- _handle_field_changes with wizissueduedate fallback key and skip_status=True
- update_remote_system_command full flow (delta+close+entries)
- get_remote_data_command with service account [SA] note format
- WizMirrorDirection.from_params() for Outgoing and invalid values
- get_resources with updated_at_before filter and both filters combined
- get_resources error message asserting new param names
- reject_or_resolve_issue comment truncation at call site
- get_filtered_detections null description fallback through integrated flow

* Fix null-safety bugs in notes/ruleMatch handling and add missing resolutionReason to outgoing mapper

- Fix _build_new_note_entries crash when API returns notes: null (.get("notes", []) → .get("notes") or [])
- Fix clear_issue_note crash on notes: null and UnboundLocalError when no notes exist
- Fix WizDefend clear_threat_comments crash on notes: null
- Fix WizDefend build_fallback_description and build_incidents crash on ruleMatch: null
- Add resolutionReason to outgoing mapper so XSOAR close reasons propagate to Wiz
- Add unit tests for _build_new_note_entries, _attach_mirror_metadata, and null edge cases

* Fix WizDefend null-safety bug for ruleMatch={"rule":None} and add reject/resolve payload-shape tests

build_fallback_description (WizDefend.py:1638) and build_incidents
(WizDefend.py:1654) crashed with `'NoneType' object has no attribute
'get'` when a detection's ruleMatch was {"rule": None}. The existing
defense `(... or {}).get(RULE, {})` only catches RULE_MATCH=None — when
the `rule` key is present with value None, .get(RULE, {}) returns None
(default only fires for missing keys). Switched both call sites to
`((... or {}).get(RULE) or {}).get(NAME)` to coerce None to {} after
each chained get.

Added regression test test_build_incidents_rulematch_rule_null covering
the {"rule": None} case (the existing test_build_incidents_rulematch_null
only covered ruleMatch=None).

Also added two symmetric payload-shape tests for the Wiz reject/resolve
path (test_reject_issue_sends_rejected_status_and_reason,
test_resolve_issue_sends_resolved_status_and_reason) that capture the
GraphQL mutation variables and assert status/resolutionReason/note are
sent correctly. Closes a coverage gap where the existing reject/resolve
tests only verified the API response was returned, not the request shape.

* Address v2.0.2 documentation audit (Layer 7)

Resolves 17 of 18 doc-audit items raised against the v2.0.2 changes.
Item #12 (wiz-get-resources structured outputs schema) deferred —
needs Wiz API response shape inspection.

Wiz README:
- Fix copy-paste descriptions: wiz-issue-in-progress, wiz-reopen-issue
  example arg name, wiz-reject-issue, wiz-get-project-team
- Document 1400-char note truncation on reject_note, resolution_note,
  set-issue-note
- Add Mirroring section: direction matrix, mirrored-fields table, loop
  prevention, truncation, and the 4 mirror-engine commands
  (get-remote-data, get-modified-remote-data, update-remote-system,
  get-mapping-fields) marked as not for manual use
- Fix mirror_limit description (was "Maximum incidents", actually page
  size) and clarify intended usage
- Generalize wiz-set-issue-due-date format hint (YYYY-MM-DD)
- Broaden platform list intro (was AWS/Azure/GCP only)

Wiz.yml:
- Generalize wiz-resolve-issue description (was "Threat Detection
  Issue", applies to any Wiz Issue)
- Replace terse "Agentless cloud security." with bidirectional-mirroring
  summary aligned with pack_metadata description

WizDefend README:
- Document 1400-char note truncation on resolve_threat resolution_note
  and set-threat-comment note
- Document the auto-generated detection description fallback format
  (`<SEVERITY> severity detection triggered by rule '<rule>' (ID: <id>)`)
- Fix First fetch maximum (was "5 days", YML says 2 days)

ReleaseNotes/2_0_2.md:
- Reconcile breaking change wording with 2_0_2.json — explicitly list
  the separate removals for wiz-reject-issue (DETECTION_EXPIRED,
  CONTROL_DISABLED, CONTROL_DELETED, OBJECT_DELETED, ISSUE_FIXED) vs
  wiz-resolve-issue (DETECTION_EXPIRED, CONTROL_DISABLED,
  CONTROL_DELETED). The previous wording lumped both commands together
  and disagreed with the JSON breakingChangesNotes field.

OBJECT_DELETED stays in wiz-resolve-issue reasons (matches existing YML
+ JSON spec); only removed from wiz-reject-issue.

* Fix mirror 5-min timeout + address v2.0.2 review findings

Mirror timeout: get_modified_remote_data was Docker-killed at 5min on any
backlog because _fetch_all_issue_nodes walked every page with the heavy
PULL_ISSUES_QUERY. Now uses a slim MODIFIED_ISSUE_IDS_QUERY (id +
statusChangedAt only), runs single-page-per-call, and persists a
mirror_cursor in integration context so backlogs drain across cycles
instead of dying in one giant call. _fetch_all_issue_nodes also gained a
240s deadline so wiz-get-issues / fetch_incidents return partial results
instead of being killed.

Review LOWs addressed:
- _build_new_note_entries used lex string comparison on ISO timestamps;
  Wiz returns microsecond precision while XSOAR's lastUpdate is
  second-precision, so '.' (0x2E) < 'Z' (0x5A) silently dropped notes
  added in the sub-second window. Now parses both with
  datetime.fromisoformat before comparing.
- _handle_outgoing_entries now re-checks comment_tag in entry.tags as
  defense-in-depth against tag-config drift.
- Extracted _safe_rule_name helper in WizDefend; two prior commits had
  to harden successive null positions in the ruleMatch.rule.name chain.
- Documented intentional first-sync empty-entries behavior in docstring
  and README Mirroring section.

Tests: 226 Wiz + 445 WizDefend passing. demisto-sdk validate clean,
xsoar-lint 9.99/10 unchanged on both files.

* Fix mirror cursor lex compare for mixed-precision ISO timestamps

XSOAR's lastUpdate is second-precision (`:00Z`) while saved_cursor is
microsecond-precision from Wiz (`:00.500000Z`). Lex `max` would pick
the bare-Z value because `Z` (0x5A) > `.` (0x2E), rewinding the cursor
by up to 999ms and re-fetching the same half-second window each cycle.

Switched both compare sites in get_modified_remote_data_command to use
the existing _parse_iso_timestamp helper. Added regression test
test_get_modified_remote_data_mixed_precision_cursor_wins.

* Bump pack version to 2.0.3 (avoid in-place 2.0.2 marketplace overwrite)

demisto/content master already shipped Wiz pack v2.0.2 via demisto#43569
("chore: delete old supported modules - part 4/4") with placeholder
release notes ("Documentation and metadata improvements"). To avoid
republishing 2.0.2 with different content on the marketplace, bump
this release to 2.0.3.

- Rename ReleaseNotes/2_0_2.{md,json} -> 2_0_3.{md,json}
- Bump pack_metadata.json currentVersion: 2.0.2 -> 2.0.3

* Clarify wiz-resolve-issue scope and improve error handling

The v2.0.2/v2.0.3 generalisation of the YAML help text to "Resolve a Wiz
Issue." over-promised: the command is only valid for Threat Detection
issues — other types (Toxic Combination, Cloud Configuration, Attack
Surface) are auto-resolved when the underlying problem is fixed and
should be closed via wiz-reject-issue.

- Wiz.yml: rewrite the wiz-resolve-issue description to make the scope
  explicit and direct users to wiz-reject-issue for other types.
- Wiz.py: fix the resolve_issue docstring (was "Reject"); guard the
  _get_issue lookup so a missing issue returns "Issue not found: <id>"
  instead of crashing with IndexError; extend the gate error message
  with the actionable wiz-reject-issue redirect.
- Wiz_test.py: add test_resolve_issue_non_threat_returns_friendly_error
  and test_resolve_issue_unknown_id_returns_not_found, both asserting
  the integration short-circuits before the update mutation.
- ReleaseNotes/2_0_3.md: describe the clarified scope and the improved
  error handling.

* Fix outgoing mirror losing analyst's chosen close reason

The round-3 outgoing mapper added `resolutionReason <- resolutionReason`
but no XSOAR incident field by that name exists on the Wiz Issue type.
On close, the delta arrived without resolutionReason, code fell back to
WONT_FIX, and Wiz silently dropped that on THREAT_DETECTION issues —
so every closed XSOAR incident lost the analyst's chosen close reason.

Discovered via live mirror cycle on incident 1360466 → Wiz
dc8dbd62-...: Wiz received status=RESOLVED but resolutionReason=None.

Fix: add `_resolve_wiz_reason(delta, data)` helper that prefers an
explicit delta.resolutionReason (forward-compat for if/when an XSOAR
custom field gets added) and falls back to translating XSOAR's built-in
closeReason via XSOAR_CLOSE_REASON_TO_WIZ. Threading data through
_handle_field_changes and _mirror_status_to_wiz so the status-flip
paths benefit from the same fallback.

Tests (Wiz_test.py: 226 → 236):
- _resolve_wiz_reason precedence + translation + unknown-value handling
- update_remote_system_command close path uses the fallback
- update_remote_system_command explicit resolutionReason still wins
- _mirror_status_to_wiz resolved-status branch uses the fallback

May warrant a v2.0.4 bump or a 2_0_3.md RN entry depending on whether
2.0.3 has shipped to marketplace yet.

* Add schema-consistency tests for mirror field declarations

Guard against the v2.0.4 phantom-source bug class: declaring a mirror
source field name (in WIZ_MIRRORED_FIELDS or in the outgoing mapper's
`simple:` source) that does not exist as either an XSOAR system field
or a pack-defined IncidentField. When that happens, XSOAR delivers
nothing in the delta and the field silently defaults — invisible to
unit tests that hand-build delta dicts.

Three new tests in Wiz_test.py:
- test_outgoing_mapper_sources_resolve_to_real_fields: walks
  classifier-mapper-outgoing-Wiz.json and asserts every `simple:` source
  is a known XSOAR built-in, a pack-defined cliName, or a documented
  exemption. Verified to fail on the original bug if the
  resolutionReason exemption is removed.
- test_wiz_mirrored_fields_resolve_to_real_fields: same check on the
  WIZ_MIRRORED_FIELDS list advertised via get_mapping_fields_command.
- test_known_phantom_exemptions_are_actually_referenced: hygiene check
  so the exemption set doesn't decay into a junk drawer.

_KNOWN_PHANTOM_SOURCES documents the three legitimate edge cases
(resolutionReason backstopped by closeReason translation, notes
mirrored via entries, dueAt as a mapper destination name) with the
specific runtime guard for each.

Wiz_test.py: 239 → 242 tests, all passing.

* Fold close-reason fix into 2.0.3 release notes; drop v2.0.4 markers

The close-reason carry-through fix and schema-consistency tests are
shipping as part of v2.0.3, not a separate v2.0.4 — the pack version
hasn't been bumped and 2.0.3 has not yet been published to marketplace.

- Add a 2_0_3.md bullet describing the user-facing close-reason fix
  (XSOAR closeReason → Wiz resolutionReason translation, prevents
  silent default to WONT_FIX on incident close).
- Drop "v2.0.4" markers from test section headers and docstrings;
  rephrase as plain REGRESSION/topic markers since these tests are
  part of the 2.0.3 release.

* Add closeReason to outgoing mapper so the runtime translation receives it

Phase 6.12b live testing exposed that the previous-commit close-reason
fix was incomplete. The runtime helper `_resolve_wiz_reason` reads
`data.get('closeReason')` but XSOAR's outgoing mirror engine only
delivers fields the OUTGOING MAPPER declares — and `closeReason` was
never declared. Diagnostic log proved it: `data.keys=['status']`,
`data.closeReason=None`, so `_resolve_wiz_reason` always returned None
and the close fell back to `WONT_FIX`.

Adding `closeReason -> closeReason` to classifier-mapper-outgoing-Wiz.json
tells the mirror engine to include `closeReason` in the args sent to
`update-remote-system`. After the change, the diag log shows:
  data.keys=['closeReason', 'status']
  data.closeReason='Resolved'
  _handle_incident_closed: ... reason=ISSUE_FIXED

GraphQL mutation sent to Wiz now carries the correct enum:
  patch={status: RESOLVED, resolutionReason: ISSUE_FIXED}

Verified live with closeReason='Resolved' (-> ISSUE_FIXED) and
closeReason='False Positive' (-> FALSE_POSITIVE). Wiz canary issues
returned NOT_FOUND on apply (Failures Entry 15 — canary-only SA scope
+ purge), but the integration's outbound payload is correct.

* Update Service Account scope list in integration setup docs

Updated the Service Account scope list in the integration setup
instructions to match the scopes required by the integration's
commands. Added a release-notes bullet under 2.0.3.

No code change.

* Run demisto-sdk format + add classifier release notes (review)

Addresses Benimanela's review comments on PR demisto#43959:

- demisto-sdk format applied to all PR-scope files. Pure key-reordering
  in classifier-Wiz_Mapper.json (severity / Wiz Issue Resolution
  Recommendation moved before dbotMirror* entries) and Wiz.yml
  (defaultmapperout, ismappable, isremotesyncin/out moved to canonical
  positions). Semantically identical.
- ReleaseNotes/2_0_3.md: added explicit "Wiz - Outgoing Mapper" and
  "Wiz Mapper" entries surfacing the user-facing classifier changes
  (new outgoing mapper for status/dueAt/closeReason/resolutionReason;
  incoming dbotMirror* mappings for mirror correlation).

* Address Copilot review: parsed_args.last_update + list-typed empty results

Two small mechanical hardenings flagged by the Copilot review on PR demisto#43959:

- get_remote_data_command: read last_update from parsed_args.last_update
  (the GetRemoteDataArgs-parsed value) instead of the raw args dict, so
  XSOAR-version casing/normalization differences can't drift the cursor.
- query_api: return [] instead of {} on no-results so callers can iterate
  without a per-call type guard. get_filtered_detections gains a defensive
  isinstance(list) check so a future non-list query result short-circuits
  cleanly instead of silently iterating dict keys.
- Updates the existing test_query_api_empty_response assertion to match.

239 Wiz + 488 WizDefend = 727 unit tests pass. demisto-sdk validate clean.

* Fix pre-commit errors

* Update .pack-ignore

* Update classifier-mapper-outgoing-Wiz.json

* Cleanup demisto.args usages in Wiz.py

---------

Co-authored-by: Ariel Tobiana <107474518+ariel-wiz@users.noreply.github.com>
Co-authored-by: Kamal Qarain <kqarain@paloaltonetworks.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant