1
+ '''
2
+ Created on Jan 25, 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,
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 HTTPError
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
+ hub_client = Client (token = config ['api_token' ],
113
+ base_url = config ['baseurl' ],
114
+ verify = not config ['insecure' ],
115
+ timeout = config ['timeout' ],
116
+ retries = config ['retries' ])
117
+ user_name = config ['user_name' ]
118
+ notification_filters = config ['filters' ]
119
+ vuln_source = config ['vuln_source' ] # Future use
120
+
121
+ user = get_user (hub_client , user_name )
122
+ if user :
123
+ number_of_seen = 0
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
+ number_of_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
+ logging .info (f"Finished. Number of Notifications which are set to SEEN is { number_of_seen } " )
135
+ else :
136
+ logging .error (f"User not found for { user_name } " )
137
+ except HTTPError as err :
138
+ hub_client .http_error_handler (err )
139
+ except Exception as err :
140
+ logging .error (f"Failed to perform the task. See the stack trace" )
141
+ traceback .print_exc ()
142
+
143
+ if __name__ == '__main__' :
144
+ sys .exit (main ())
0 commit comments