Skip to content

Commit

Permalink
XDR-19630 XDR-19632 Fix interval logic for new statuses. (#1468)
Browse files Browse the repository at this point in the history
* XDR-19630 XDR-19632 Fix interval logic for new statuses.

In my previous PR I had missed that the logic for recording the new status
changes in :incident_time was also needed (and necessary for interval computation).

See the main PR for details: #1467

I also took the opportunity to make a change suggested by @ereteog in the previous PR to use regex's instead of sets to recognize 'New', 'Open', and 'Closed' statuses.
  • Loading branch information
samwagg authored and jonathan-eaton-cisco committed Feb 11, 2025
1 parent afa7858 commit ba0beec
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 44 deletions.
49 changes: 26 additions & 23 deletions src/ctia/entity/incident.clj
Original file line number Diff line number Diff line change
Expand Up @@ -113,21 +113,31 @@
(s/defschema IncidentStatusUpdate
{:status IncidentStatus})

(defn new-status? [status]
(some? (re-matches #"New(: .+)?" status)))

(defn open-status? [status]
(some? (re-matches #"Open(: .+)?" status)))

(defn closed-status? [status]
(some? (re-matches #"Closed(: .+)?" status)))

(defn make-status-update
[{:keys [status]}]
(let [t (time/internal-now)
verb (case status
"New" nil
"Stalled" nil
"Hold" nil
;; Note: GitHub syntax highlighting doesn't like lists with strings
"Containment Achieved" :remediated
"Restoration Achieved" :remediated
"Open" :opened
"Rejected" :rejected
"Closed" :closed
"Incident Reported" :reported
nil)]
verb (or (case status
"New" nil
"Stalled" nil
"Hold" nil
;; Note: GitHub syntax highlighting doesn't like lists with strings
"Containment Achieved" :remediated
"Restoration Achieved" :remediated
"Rejected" :rejected
"Incident Reported" :reported
nil)
(cond
(open-status? status) :opened
(closed-status? status) :closed))]
(cond-> {:status status}
verb (assoc :incident_time {verb t}))))

Expand All @@ -143,13 +153,6 @@
(assoc-in [:intervals interval]
(jt/time-between (jt/instant earlier) (jt/instant later) :seconds))))

(def new-status-set #{"New" "New: Processing" "New: Presented"})

(def open-status-set #{"Open" "Open: Investigating" "Open: Reported" "Open: Contained" "Open: Recovered"})

(def closed-status-set #{"Closed" "Closed: Under Review" "Closed: Confirmed Threat" "Closed: Suspected"
"Closed: False Positive" "Closed: Near-Miss" "Closed: Other" "Closed: Merged"})

(s/defn compute-intervals :- ESStoredIncident
"Given the currently stored (raw) incident and the incident to update it to, return a new update
that also computes any relevant intervals that are missing from the updated incident."
Expand All @@ -163,14 +166,14 @@
(cond-> incident
;; the duration between the time at which the incident changed from New to Open and the incident creation time
;; https://github.com/advthreat/iroh/issues/7622#issuecomment-1496374419
(and (new-status-set old-status)
(open-status-set new-status))
(and (new-status? old-status)
(open-status? new-status))
(update-interval :new_to_opened
(:created prev)
(get-in incident [:incident_time :opened]))

(and (open-status-set old-status)
(closed-status-set new-status))
(and (open-status? old-status)
(closed-status? new-status))
(update-interval :opened_to_closed
;; we assume this was updated by the status route on Open. will be garbage if status was updated
;; in any other way.
Expand Down
48 changes: 27 additions & 21 deletions test/ctia/entity/incident_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -809,37 +809,43 @@
first-opened_to_closed 150 ;;offsets in seconds from opened to closed
second-opened_to_closed 250
third-opened_to_closed 550
incident->intervals (take 3 [[(assoc (gen-new-incident) :status "New" :title "incident1")
{:created first-created
:new_to_opened first-new_to_opened
:opened_to_closed first-opened_to_closed}]
[(assoc (gen-new-incident) :status "New" :title "incident2")
{:created second-created
:new_to_opened second-new_to_opened
:opened_to_closed second-opened_to_closed}]
[(assoc (gen-new-incident) :status "New" :title "incident3")
{:created third-created
:new_to_opened third-new_to_opened
:opened_to_closed third-opened_to_closed}]])
incident->status-changes (take 3 [[(assoc (gen-new-incident) :status "New" :title "incident1")
{:created first-created
:new_to_opened first-new_to_opened
:opened-status "Open"
:opened_to_closed first-opened_to_closed
:closed-status "Closed"}]
[(assoc (gen-new-incident) :status "New: Processing" :title "incident2")
{:created second-created
:new_to_opened second-new_to_opened
:opened-status "Open: Reported"
:opened_to_closed second-opened_to_closed
:closed-status "Closed: False Positive"}]
[(assoc (gen-new-incident) :status "New: Presented" :title "incident3")
{:created third-created
:new_to_opened third-new_to_opened
:opened-status "Open: Investigating"
:opened_to_closed third-opened_to_closed
:closed-status "Closed: Merged"}]])
+sec #(jt/plus epoch (jt/seconds %))
incident-ids (mapv (fn [[incident {:keys [created]}]]
;; create extra incidents whose statuses are never changed to simulate a real environment
(second
(repeatedly
2
#(create-incident-at-time app (jt/java-date (+sec created)) incident))))
incident->intervals)
incident-id->incident+intervals (map vector incident-ids incident->intervals)
_ (assert (= (count incident-id->incident+intervals) (count incident->intervals))
incident->status-changes)
incident-id->incident+intervals (map vector incident-ids incident->status-changes)
_ (assert (= (count incident-id->incident+intervals) (count incident->status-changes))
incident-ids)
_ (testing "populate intervals"
(doseq [[incident-id [incident {:keys [created new_to_opened opened_to_closed]}]] incident-id->incident+intervals
[new-status next-time] [["Open" (+sec (+ created new_to_opened))]
["Closed" (+sec (+ created new_to_opened opened_to_closed))]]]
(doseq [[incident-id [incident {:keys [created new_to_opened opened-status opened_to_closed closed-status]}]] incident-id->incident+intervals
[new-status next-time] [[opened-status (+sec (+ created new_to_opened))]
[closed-status (+sec (+ created new_to_opened opened_to_closed))]]]
(testing (pr-str [new-status incident])
(let [{:keys [parsed-body] :as response} (helpers/fixture-with-fixed-time
(jt/java-date next-time)
#(post-status app (uri/uri-encode incident-id) new-status))]
(let [response (helpers/fixture-with-fixed-time
(jt/java-date next-time)
#(post-status app (uri/uri-encode incident-id) new-status))]
(assert (= 200 (:status response))
(pr-str response))))))
avg #(quot (apply + %&) (count %&))]
Expand Down

0 comments on commit ba0beec

Please sign in to comment.