Skip to content

Commit 0fd74ab

Browse files
dandyecopybara-github
authored andcommitted
v1alpha samples for get alert and update alert(s)
PiperOrigin-RevId: 648722728
1 parent 65385f4 commit 0fd74ab

File tree

2 files changed

+287
-0
lines changed

2 files changed

+287
-0
lines changed

detect/v1alpha/legacy_get_alert.py

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#!/usr/bin/env python3
2+
3+
# Copyright 2024 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
"""Get Alert details (legacy)."""
18+
import argparse
19+
import json
20+
from typing import Dict
21+
22+
from common import chronicle_auth
23+
from common import project_id
24+
from common import project_instance
25+
from common import regions
26+
27+
from google.auth.transport import requests
28+
29+
CHRONICLE_API_BASE_URL = "https://chronicle.googleapis.com"
30+
SCOPES = [
31+
"https://www.googleapis.com/auth/cloud-platform",
32+
]
33+
34+
35+
def legacy_get_alert(
36+
http_session: requests.AuthorizedSession,
37+
proj_id: str,
38+
proj_instance: str,
39+
proj_region: str,
40+
alert_id: str,
41+
) -> Dict[str, any]:
42+
"""Get Alert details (legacy).
43+
44+
Args:
45+
http_session: Authorized session for HTTP requests.
46+
proj_id: GCP project id or number to which the target instance belongs.
47+
proj_instance: Customer ID (uuid with dashes) for the Chronicle instance.
48+
proj_region: region in which the target project is located.
49+
alert_id: identifier for the alert to get.
50+
Returns:
51+
Dictionary representation of the alert.
52+
53+
Raises:
54+
requests.exceptions.HTTPError: HTTP request resulted in an error
55+
(response.status_code >= 400).
56+
"""
57+
base_url_with_region = regions.url_always_prepend_region(
58+
CHRONICLE_API_BASE_URL,
59+
proj_region
60+
)
61+
# pylint: disable-next=line-too-long
62+
parent = f"projects/{proj_id}/locations/{proj_region}/instances/{proj_instance}"
63+
url = f"{base_url_with_region}/v1alpha/{parent}/legacy:legacyGetAlert"
64+
65+
params = {
66+
"alertId": alert_id,
67+
}
68+
69+
response = http_session.request("GET", url, params=params)
70+
71+
if response.status_code >= 400:
72+
print(response.text)
73+
response.raise_for_status()
74+
return response.json()
75+
76+
77+
if __name__ == "__main__":
78+
79+
parser = argparse.ArgumentParser()
80+
chronicle_auth.add_argument_credentials_file(parser)
81+
project_instance.add_argument_project_instance(parser)
82+
project_id.add_argument_project_id(parser)
83+
regions.add_argument_region(parser)
84+
parser.add_argument(
85+
"--alert_id", type=str, required=True,
86+
help="Id for the alert"
87+
)
88+
args = parser.parse_args()
89+
90+
auth_session = chronicle_auth.initialize_http_session(
91+
args.credentials_file,
92+
SCOPES,
93+
)
94+
alerts = legacy_get_alert(
95+
auth_session,
96+
args.project_id,
97+
args.project_instance,
98+
args.region,
99+
args.alert_id,
100+
)
101+
print(json.dumps(alerts, indent=2))

detect/v1alpha/legacy_update_alert.py

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
#!/usr/bin/env python3
2+
3+
# Copyright 2024 Google LLC
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
"""Update one alert or a file of alert IDs."""
18+
import argparse
19+
import json
20+
from typing import Dict
21+
22+
from common import chronicle_auth
23+
from common import project_id
24+
from common import project_instance
25+
from common import regions
26+
27+
from google.auth.transport import requests
28+
29+
SCOPES = [
30+
"https://www.googleapis.com/auth/cloud-platform",
31+
]
32+
PRIORITY_ENUM = (
33+
"PRIORITY_UNSPECIFIED",
34+
"PRIORITY_INFO",
35+
"PRIORITY_LOW",
36+
"PRIORITY_MEDIUM",
37+
"PRIORITY_HIGH",
38+
"PRIORITY_CRITICAL",
39+
)
40+
REASON_ENUM = (
41+
"REASON_UNSPECIFIED",
42+
"REASON_NOT_MALICIOUS",
43+
"REASON_MALICIOUS",
44+
"REASON_MAINTENANCE",
45+
)
46+
REPUTATION_ENUM = (
47+
"REPUTATION_UNSPECIFIED",
48+
"USEFUL",
49+
"NOT_USEFUL",
50+
)
51+
STATUS_ENUM = (
52+
"STATUS_UNSPECIFIED",
53+
"NEW",
54+
"REVIEWED",
55+
"CLOSED",
56+
"OPEN",
57+
)
58+
VERDICT_ENUM = (
59+
"VERDICT_UNSPECIFIED",
60+
"TRUE_POSITIVE",
61+
"FALSE_POSITIVE",
62+
)
63+
DEFAULT_FEEDBACK = {
64+
# These use a controlled vocab. See enums above.
65+
"reason": "REASON_MAINTENANCE",
66+
"reputation": "REPUTATION_UNSPECIFIED",
67+
"status": "CLOSED",
68+
"verdict": "VERDICT_UNSPECIFIED",
69+
# free text (not controlled vocab)
70+
"comment": "automated cleanup",
71+
"rootCause": "Other",
72+
}
73+
74+
75+
def legacy_update_alert(
76+
http_session: requests.AuthorizedSession,
77+
proj_id: str,
78+
proj_instance: str,
79+
proj_region: str,
80+
alert_id: str,
81+
feedback: Dict[str, str] = None,
82+
) -> Dict[str, any]:
83+
"""Legacy Update an Alert.
84+
85+
Args:
86+
http_session: Authorized session for HTTP requests.
87+
proj_id: GCP project id or number to which the target instance belongs.
88+
proj_instance: Customer ID (uuid with dashes) for the Chronicle instance.
89+
proj_region: region in which the target project is located.
90+
alert_id: identifier for the alert to be closed.
91+
feedback: (optional) dictionary containing the feedback key/values.
92+
93+
Returns:
94+
response.json
95+
96+
Raises:
97+
requests.exceptions.HTTPError: HTTP request resulted in an error
98+
(response.status_code >= 400).
99+
"""
100+
# pylint: disable=line-too-long
101+
parent = f"projects/{proj_id}/locations/{proj_region}/instances/{proj_instance}"
102+
url = f"https://{proj_region}-chronicle.googleapis.com/v1alpha/{parent}/legacy:legacyUpdateAlert"
103+
# pylint: enable=line-too-long
104+
105+
if not feedback:
106+
feedback = {}
107+
reason = feedback.get("reason") or DEFAULT_FEEDBACK["reason"]
108+
reputation = feedback.get("reputation") or DEFAULT_FEEDBACK["reputation"]
109+
status = feedback.get("status") or DEFAULT_FEEDBACK["status"]
110+
verdict = feedback.get("verdict") or DEFAULT_FEEDBACK["verdict"]
111+
112+
# enum validation
113+
if reason not in REASON_ENUM:
114+
raise ValueError(f"Reason {reason} not in {REASON_ENUM}")
115+
if reputation not in REPUTATION_ENUM:
116+
raise ValueError(f"Reputation {reputation} not in {REPUTATION_ENUM}")
117+
if status not in STATUS_ENUM:
118+
raise ValueError(f"Status {status} not in {STATUS_ENUM}")
119+
if verdict not in VERDICT_ENUM:
120+
raise ValueError(f"Verdict {verdict} not in {VERDICT_ENUM}")
121+
122+
comment = feedback.get("comment") or DEFAULT_FEEDBACK["comment"]
123+
root_cause = feedback.get("rootCause") or DEFAULT_FEEDBACK["rootCause"]
124+
125+
body = {
126+
"alertId": alert_id,
127+
"feedback": {
128+
"comment": comment,
129+
"reason": reason,
130+
"reputation": reputation,
131+
"rootCause": root_cause,
132+
"status": status,
133+
"verdict": verdict,
134+
},
135+
}
136+
response = http_session.request("POST", url, json=body)
137+
if response.status_code >= 400:
138+
print(response.text)
139+
response.raise_for_status()
140+
return response.json()
141+
142+
143+
if __name__ == "__main__":
144+
parser = argparse.ArgumentParser()
145+
chronicle_auth.add_argument_credentials_file(parser)
146+
project_instance.add_argument_project_instance(parser)
147+
project_id.add_argument_project_id(parser)
148+
regions.add_argument_region(parser)
149+
# local
150+
group = parser.add_mutually_exclusive_group(required=True)
151+
group.add_argument(
152+
"--alert_id",
153+
type=str,
154+
help="ID of the Alert.")
155+
group.add_argument(
156+
"--alert_ids_file",
157+
type=str,
158+
help="File with one Alert ID per line.")
159+
args = parser.parse_args()
160+
161+
# pylint: disable-next=line-too-long
162+
auth_session = chronicle_auth.initialize_http_session(
163+
args.credentials_file,
164+
SCOPES
165+
)
166+
if args.alert_id:
167+
resp = legacy_update_alert(
168+
auth_session,
169+
args.project_id,
170+
args.project_instance,
171+
args.region,
172+
args.alert_id,
173+
)
174+
print(json.dumps(resp, indent=2))
175+
elif args.alert_ids_file:
176+
with open(args.alert_ids_file) as fh:
177+
for an_id in fh:
178+
print(f"working on alert id: {an_id}")
179+
resp = legacy_update_alert(
180+
auth_session,
181+
args.project_id,
182+
args.project_instance,
183+
args.region,
184+
an_id.strip(),
185+
)
186+
print(json.dumps(resp, indent=2))

0 commit comments

Comments
 (0)