Skip to content

Commit c607c77

Browse files
authored
Merge pull request #234 from blackducksoftware/koshmack/dev-filtering_notifications
Koshmack/dev filtering notifications
2 parents 4ffd6c9 + 2a8a483 commit c607c77

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed

examples/filtering_notifications.py

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
'''
2+
Created on Feb 3, 2023
3+
4+
@author: mkoishi
5+
6+
This script sets the not-interested notifications state for the appointed user to "SEEN" to free up the user from the notification flood.
7+
The notification "type," which is either POLICY_OVERRIDE, or RULE_VIOLATION, or VULNERABILITY, or something else, determines the filter.
8+
If the notification type is VULNERABILITY, it also checks optionally to see if the vulnerability is new.
9+
10+
Usage:
11+
The following items must be filled out in the.restconfig-notifications.json file.
12+
Note:
13+
1. "type" can take POLICY_OVERRIDE or RULE_VIOLATION or VULNERABILITY or else. Please refer to the Black Duck REST API documentation.
14+
2. "vuln_source" is for future use.
15+
16+
{
17+
"baseurl": "YOUR_BLACKDUCK_URL",
18+
"api_token": "YOUR_API_TOKEN",
19+
"insecure": <true or false>,
20+
"timeout": 15.0,
21+
"retries": 3,
22+
"filters": {
23+
"type":"VULNERABILITY",
24+
"only_new_vulns": <true or false>
25+
},
26+
"vuln_source": "NVD",
27+
"user_name": "YOUR_USER_NAME"
28+
}
29+
30+
Remarks:
31+
If the number of the notifications is large, we may need to run this script a few rounds to fetch all notifications from Black Duck.
32+
Since Client class of hub-rest-api-python uses a generator, we should not consider the pagination, and all notifications should be
33+
returned by one attempt. It might be affected by the Black Duck side's buffer or else. It will require more research. Until then, please
34+
run this script until the number of the fetched notifications becomes zero.
35+
36+
Copyright (C) 2023 Synopsys, Inc.
37+
http://www.synopsys.com/
38+
39+
Licensed to the Apache Software Foundation (ASF) under one
40+
or more contributor license agreements. See the NOTICE file
41+
distributed with this work for additional information
42+
regarding copyright ownership. The ASF licenses this file
43+
to you under the Apache License, Version 2.0 (the
44+
"License"); you may not use this file except in compliance
45+
with the License. You may obtain a copy of the License at
46+
47+
http://www.apache.org/licenses/LICENSE-2.0
48+
49+
Unless required by applicable law or agreed to in writing,
50+
software distributed under the License is distributed on an
51+
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
52+
KIND, either express or implied. See the License for the
53+
specific language governing permissions and limitations
54+
under the License.
55+
'''
56+
57+
import logging
58+
import sys
59+
import json
60+
import traceback
61+
import re
62+
from requests import RequestException
63+
from blackduck import Client
64+
from ast import literal_eval
65+
66+
def log_config():
67+
logging.basicConfig(format='%(asctime)s:%(levelname)s:%(module)s: %(message)s', stream=sys.stderr, level=logging.INFO)
68+
logging.getLogger("requests").setLevel(logging.WARNING)
69+
logging.getLogger("urllib3").setLevel(logging.WARNING)
70+
logging.getLogger("blackduck").setLevel(logging.WARNING)
71+
72+
def read_notifications(hub_client, notification_filters, user, vuln_source=None):
73+
params = {
74+
"filter": "notificationState:NEW"
75+
}
76+
for notification in hub_client.get_resource("notifications", user, params=params):
77+
logging.debug(f"Fetched notification item is {notification}")
78+
# Check if notification is NOT interested one and yield to get notification state to SEEN
79+
if not check_notificatons(notification, notification_filters, vuln_source):
80+
yield notification
81+
return
82+
83+
def get_user(hub_client, user_name):
84+
for user in hub_client.get_resource("users"):
85+
if user['userName'] == user_name:
86+
return user
87+
return None
88+
89+
def check_notificatons(notification, filters, vuln_source):
90+
# TODO: to check source of vulnerabilities, i.e. NVD or BDSA
91+
if notification['type'] == filters['type']:
92+
if filters['type'] == "VULNERABILITY" and filters['only_new_vulns']:
93+
if notification['content']['newVulnerabilityCount'] != 0:
94+
return notification
95+
else:
96+
return notification
97+
98+
return None
99+
100+
def update_user_notification(hub_client, user_id, notification):
101+
notification_id = re.split("/", notification['_meta']['href'])[-1]
102+
put_data = {
103+
"notificationState": "SEEN"
104+
}
105+
return hub_client.session.put(f"/api/users/{user_id}/notifications/{notification_id}", json=put_data)
106+
107+
def main():
108+
log_config()
109+
try:
110+
with open('.restconfig-notifications.json','r') as f:
111+
config = json.load(f)
112+
set_to_seen = 0
113+
hub_client = Client(token=config['api_token'],
114+
base_url=config['baseurl'],
115+
verify=not config['insecure'],
116+
timeout=config['timeout'],
117+
retries=config['retries'])
118+
user_name = config['user_name']
119+
notification_filters = config['filters']
120+
vuln_source = config['vuln_source'] # Future use
121+
122+
user = get_user(hub_client, user_name)
123+
if user:
124+
for notification in read_notifications(
125+
hub_client,
126+
notification_filters,
127+
user,
128+
vuln_source=vuln_source):
129+
user_id = re.split("/", user['_meta']['href'])[-1]
130+
res = update_user_notification(hub_client, user_id, notification)
131+
set_to_seen += 1
132+
content = literal_eval(res.content.decode("UTF-8"))
133+
logging.info(f"Notification state is set to SEEN for {content['type']}")
134+
else:
135+
logging.error(f"User not found for {user_name}")
136+
except RequestException as err:
137+
logging.error(f"Failed to read or update notification and the reason is {str(err)}")
138+
except Exception as err:
139+
logging.error(f"Failed to perform the task with exception {str(err)}. See also the stack trace")
140+
traceback.print_exc()
141+
finally:
142+
logging.info(f"=== Updating Notifications Finished ===")
143+
logging.info(f"{set_to_seen} Notifications are set to SEEN.")
144+
145+
if __name__ == '__main__':
146+
sys.exit(main())

0 commit comments

Comments
 (0)