Skip to content

Commit 1950f72

Browse files
Add support for audit report (closes #111)
1 parent d379b72 commit 1950f72

File tree

7 files changed

+146
-27
lines changed

7 files changed

+146
-27
lines changed

docs/analytics.md

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ The following methods call Veracode REST APIs and return JSON.
66

77
1. The Reporting API is available to Veracode customers by request. More information about the API is available in the [Veracode Docs](https://docs.veracode.com/r/Reporting_REST_API).
88

9-
- `Analytics().create_report(report_type ('findings'),last_updated_start_date(opt), last_updated_end_date (opt), scan_type (opt), finding_status(opt), passed_policy(opt), policy_sandbox(opt), application_id(opt), rawjson(opt), deletion_start_date(opt), deletion_end_date(opt))`: set up a request for a report. By default this command returns the GUID of the report request; specify `rawjson=True` to get the full response. Dates should be specified as `YYYY-MM-DD HH:MM:SS` with the timestamp optional. Options include:
10-
- `report_type`: required, currently supports `findings`, `scans`, and `deletedscans`.
9+
- `Analytics().create_report(report_type ('findings'),last_updated_start_date(opt), last_updated_end_date (opt), scan_type (opt), finding_status(opt), passed_policy(opt), policy_sandbox(opt), application_id(opt), rawjson(opt), deletion_start_date(opt), deletion_end_date(opt), start_date(opt), end_date(opt), audit_action(opt), target_user_id(opt), modifier_user_id(opt))`: set up a request for a report. By default this command returns the GUID of the report request; specify `rawjson=True` to get the full response. Dates should be specified as `YYYY-MM-DD HH:MM:SS` with the timestamp optional. Options include:
10+
- `report_type`: required, currently supports `findings`, `scans`, `deletedscans`, and `audit`.
1111
- `last_updated_start_date`: required for `findings` report type, beginning of date range for new or changed findings or scans
1212
- `last_updated_end_date`: optional, end of date range for new or changed findings or scans
1313
- `scan_type`: optional, one or more of 'Static Analysis', 'Dynamic Analysis', 'Manual', 'Software Composition Analysis', 'SCA'. `SCA` is only supported for the `findings` report type.
@@ -19,6 +19,43 @@ The following methods call Veracode REST APIs and return JSON.
1919
- `deletion_start_date`: required for `deletedscans` report type, beginning of date range for deleted scans.
2020
- `deletion_end_date`: optional, end of date range for deleted scans.
2121
- `sandbox_ids`: optional, array of sandbox IDs (integers) for which to return results
22+
- `start_date`: required for `audit` report type, beginning of date range for audit events
23+
- `end_date`: optional for `audit` report type, end of date range for audit events
24+
- `audit_action`: optional for `audit` report type. An array of audit actions to include in the report. Examples include `Login`, `Login Account`, `Auth`, `Create`, `Delete`, `Update`
25+
- `target_user_id`: optional for `audit` report type. The numeric user id of the user for which you want to retrieve audit events that happened to the user.
26+
- `modifier_user_id`: optional for `audit` report type. The numeric user id of the user for which you want to retrieve audit events that were caused by the user.
27+
28+
- `Analytics().create_findings_report(last_updated_start_date(opt), last_updated_end_date (opt), scan_type (opt), finding_status(opt), passed_policy(opt), policy_sandbox(opt), application_id(opt), rawjson(opt))`: set up a request for a findings report. By default this command returns the GUID of the report request; specify `rawjson=True` to get the full response. Dates should be specified as `YYYY-MM-DD HH:MM:SS` with the timestamp optional. Options include:
29+
- `start_date`: required, beginning of date range for findings
30+
- `end_date`: optional, end of date range for findings
31+
- `scan_type`: optional, one or more of 'Static Analysis', 'Dynamic Analysis', 'Manual', 'Software Composition Analysis', 'SCA'.
32+
- `finding_status`: optional, 'Open' or 'Closed'. Applies only to the `findings` report.
33+
- `passed_policy`: optional, boolean. Applies only to the `findings` report.
34+
- `policy_sandbox`: optional, 'Policy' or 'Sandbox'
35+
- `application_id`: optional. The numeric application id of an application whose findings you want to include in the report.
36+
- `rawjson`: optional, defaults to False. Returns full response if True, the GUID of the request if false
37+
38+
- `Analytics().create_scans_report(start_date, end_date(opt), scan_type(opt), policy_sandbox(opt), application_id(opt), rawjson(opt))`: set up a request for a scans report. By default this command returns the GUID of the report request; specify `rawjson=True` to get the full response. Dates should be specified as `YYYY-MM-DD HH:MM:SS` with the timestamp optional. Options include:
39+
- `start_date`: required, beginning of date range for scans
40+
- `end_date`: optional, end of date range for scans
41+
- `scan_type`: optional, one or more of 'Static Analysis', 'Dynamic Analysis', 'Manual'.
42+
- `policy_sandbox`: optional, 'Policy' or 'Sandbox'
43+
- `application_id`: optional. The numeric application id of an application whose scans you want to include in the report.
44+
- `rawjson`: optional, defaults to False. Returns full response if True, the GUID of the request if false
45+
46+
- `Analytics().create_deleted_scans_report(start_date, end_date(opt), application_id(opt),rawjson(opt))`: set up a request for a deleted scans report. By default this command returns the GUID of the report request; specify `rawjson=True` to get the full response. Dates should be specified as `YYYY-MM-DD HH:MM:SS` with the timestamp optional. Options include:
47+
- `start_date`: required, beginning of date range for deleted scans
48+
- `end_date`: optional, end of date range for deleted scans
49+
- `application_id`: optional. The numeric application id of an application whose deleted scans you want to include in the report.
50+
- `rawjson`: optional, defaults to False. Returns full response if True, the GUID of the request if false
51+
52+
- `Analytics().create_audit_report(start_date, end_date(opt), audit_action(opt), target_user_id(opt), modifier_user_id(opt),rawjson(opt))`: set up a request for an audit log report. By default this command returns the GUID of the report request; specify `rawjson=True` to get the full response. Dates should be specified as `YYYY-MM-DD HH:MM:SS` with the timestamp optional. Options include:
53+
- `start_date`: required, beginning of date range for audit events
54+
- `end_date`: optional, end of date range for audit events
55+
- `audit_action`: optional. An array of audit actions to include in the report. Examples include `Success`, `Logged out`, `Create`, `Delete`, `Update`
56+
- `target_user_id`: optional. The numeric user id of the user for which you want to retrieve audit events that happened to the user.
57+
- `modifier_user_id`: optional. The numeric user id of the user for which you want to retrieve audit events that were caused by the user.
58+
- `rawjson`: optional, defaults to False. Returns full response if True, the GUID of the request if false
2259

2360
- `Analytics().get(guid, report_type(findings))`: check the status of the report request and return the report contents when ready. Note that this method returns a tuple of `status` (string) and `results` (list); when `status` is `COMPLETED`, the `results` list will populate with results. Also, you need to specify the type of data expected by the GUID with `report_type`; this defaults to `findings`.
2461

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = 'veracode_api_py'
3-
version = '0.9.61'
3+
version = '0.9.62'
44
authors = [ {name = "Tim Jarrett", email="[email protected]"} ]
55
description = 'Python helper library for working with the Veracode APIs. Handles retries, pagination, and other features of the modern Veracode REST APIs.'
66
readme = 'README.md'
@@ -22,4 +22,4 @@ dependencies = {file = ["requirements.txt"]}
2222
[project.urls]
2323
"Homepage" = "https://github.com/veracode/veracode-api-py"
2424
"Bug Tracker" = "https://github.com/veracode/veracode-api-py/issues"
25-
"Download" = "https://github.com/veracode/veracode-api-py/archive/v_0961.tar.gz"
25+
"Download" = "https://github.com/veracode/veracode-api-py/archive/v_0962.tar.gz"
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import time
2+
import sys
3+
import json
4+
import datetime
5+
from veracode_api_py import Analytics
6+
7+
wait_seconds = 15
8+
9+
print('Generating audit report...')
10+
theguid = Analytics().create_report(report_type="audit", start_date='2025-05-01',end_date='2025-05-05')
11+
12+
print('Checking status for report {}...'.format(theguid))
13+
thestatus,theaudits=Analytics().get_audits(theguid)
14+
15+
while thestatus != 'COMPLETED':
16+
print('Waiting {} seconds before we try again...'.format(wait_seconds))
17+
time.sleep(wait_seconds)
18+
print('Checking status for report {}...'.format(theguid))
19+
thestatus,theaudits=Analytics().get_audits(theguid)
20+
21+
recordcount = len(theaudits)
22+
23+
print('Retrieved {} audit records'.format(recordcount))
24+
25+
if recordcount > 0:
26+
now = datetime.datetime.now().astimezone()
27+
filename = 'report-{}'.format(now)
28+
with open('{}.json'.format(filename), 'w') as outfile:
29+
json.dump(theaudits,outfile)
30+
outfile.close()
31+
32+
print('Wrote {} audit records to {}.json'.format(recordcount,filename))

samples/reportingapi_deleted_sample.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
wait_seconds = 15
88

99
print('Generating deleted scans report...')
10-
theguid = Analytics().create_report(report_type="deletedscans",deletion_start_date='2024-07-01',deletion_end_date='2024-12-31')
10+
theguid = Analytics().create_deleted_scans_report(start_date='2024-07-01',end_date='2024-12-31')
1111

1212
print('Checking status for report {}...'.format(theguid))
1313
thestatus,thescans=Analytics().get_deleted_scans(theguid)
@@ -20,7 +20,7 @@
2020

2121
recordcount = len(thescans)
2222

23-
print('Retrieved {} scans'.format(recordcount))
23+
print('Retrieved {} deleted scans'.format(recordcount))
2424

2525
if recordcount > 0:
2626
now = datetime.datetime.now().astimezone()
@@ -29,4 +29,4 @@
2929
json.dump(thescans,outfile)
3030
outfile.close()
3131

32-
print('Wrote {} findings to {}.json'.format(recordcount,filename))
32+
print('Wrote {} deleted scan records to {}.json'.format(recordcount,filename))

samples/reportingapi_sample.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
wait_seconds = 15
88

99
print('Generating report...')
10-
theguid = Analytics().create_report(report_type="findings",last_updated_start_date="2023-07-01 00:00:00")
10+
theguid = Analytics().create_findings_report(start_date="2023-07-01 00:00:00")
1111

1212
print('Checking status for report {}...'.format(theguid))
1313
thestatus,thefindings=Analytics().get(theguid)

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
setup(
88
name = 'veracode_api_py',
99
packages = ['veracode_api_py'],
10-
version = '0.9.61',
10+
version = '0.9.62',
1111
license='MIT',
1212
description = 'Python helper library for working with the Veracode APIs. Handles retries, pagination, and other features of the modern Veracode REST APIs.',
1313
long_description = long_description,
1414
long_description_content_type="text/markdown",
1515
author = 'Tim Jarrett',
1616
author_email = '[email protected]',
1717
url = 'https://github.com/tjarrettveracode',
18-
download_url = 'https://github.com/veracode/veracode-api-py/archive/v_0961.tar.gz',
18+
download_url = 'https://github.com/veracode/veracode-api-py/archive/v_0962.tar.gz',
1919
keywords = ['veracode', 'veracode-api'],
2020
install_requires=[
2121
'veracode-api-signing'

veracode_api_py/analytics.py

Lines changed: 67 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from .apihelper import APIHelper
77

88
class Analytics():
9-
report_types = [ "findings", "scans", "deletedscans" ]
9+
report_types = [ "findings", "scans", "deletedscans", "audit" ]
1010

1111
findings_scan_types = ["Static Analysis", "Dynamic Analysis", "Manual", "SCA", "Software Composition Analysis" ]
1212
scan_scan_types = ["Static Analysis", "Dynamic Analysis", "Manual" ]
@@ -17,30 +17,42 @@ class Analytics():
1717
def create_report(self,report_type,last_updated_start_date=None,last_updated_end_date=None,
1818
scan_type:list = [], finding_status=None,passed_policy=None,
1919
policy_sandbox=None,application_id=None,rawjson=False, deletion_start_date=None,
20-
deletion_end_date=None, sandbox_ids:list = []):
20+
deletion_end_date=None, sandbox_ids:list = [], start_date=None, end_date=None,
21+
audit_action:list = [], target_user_id:int = None, modifier_user_id:int = None):
2122

2223
if report_type not in self.report_types:
2324
raise ValueError("{} is not in the list of valid report types ({})".format(report_type,self.report_types))
2425

2526
report_def = { 'report_type': report_type }
2627

27-
if last_updated_start_date:
28-
report_def['last_updated_start_date'] = last_updated_start_date
29-
else:
30-
if report_type in ['findings','scans']:
28+
if report_type in ['audit']:
29+
if start_date:
30+
report_def['start_date'] = start_date
31+
else:
32+
raise ValueError("{} report type requires a start date.").format(report_type)
33+
34+
if end_date:
35+
report_def['end_date'] = end_date
36+
37+
if report_type in ['findings','scans']:
38+
if last_updated_start_date:
39+
report_def['last_updated_start_date'] = last_updated_start_date
40+
else:
3141
raise ValueError("{} report type requires a last updated start date.").format(report_type)
3242

33-
if last_updated_end_date:
34-
report_def['last_updated_end_date'] = last_updated_end_date
35-
36-
if deletion_end_date:
37-
report_def['deletion_end_date'] = deletion_end_date
43+
if last_updated_end_date:
44+
report_def['last_updated_end_date'] = last_updated_end_date
3845

39-
if deletion_start_date:
40-
report_def['deletion_start_date'] = deletion_start_date
41-
else:
42-
if report_type == 'deletedscans':
43-
raise ValueError("{} report type requires a deleteion start date.").format(report_type)
46+
if report_type == 'deletedscans':
47+
if deletion_start_date:
48+
report_def['deletion_start_date'] = deletion_start_date
49+
else:
50+
raise ValueError("{} report type requires a deletion start date.").format(report_type)
51+
52+
if deletion_end_date:
53+
report_def['deletion_end_date'] = deletion_end_date
54+
55+
# clean this part up, make it object oriented, probably switch report creation by report type and create sub methods
4456

4557
if len(scan_type) > 0:
4658
if report_type == 'findings':
@@ -65,6 +77,15 @@ def create_report(self,report_type,last_updated_start_date=None,last_updated_end
6577

6678
if sandbox_ids:
6779
report_def['sandbox_ids'] = sandbox_ids
80+
81+
if len(audit_action) > 0:
82+
report_def['audit_action'] = audit_action
83+
84+
if target_user_id:
85+
report_def['target_user_id'] = target_user_id
86+
87+
if modifier_user_id:
88+
report_def['modifier_user_id'] = modifier_user_id
6889

6990
payload = json.dumps(report_def)
7091
response = APIHelper()._rest_request(url=self.base_url,method="POST",body=payload)
@@ -74,6 +95,31 @@ def create_report(self,report_type,last_updated_start_date=None,last_updated_end
7495
else:
7596
return response['_embedded']['id'] #we will usually just need the guid so we can come back and fetch the report
7697

98+
def create_findings_report(self, start_date, end_date=None,
99+
scan_type:list = [], finding_status=None, passed_policy=None,
100+
policy_sandbox=None, application_id=None,rawjson=False):
101+
return self.create_report(report_type='findings', last_updated_start_date=start_date,
102+
last_updated_end_date=end_date,scan_type=scan_type,
103+
finding_status=finding_status, passed_policy=passed_policy,
104+
policy_sandbox=policy_sandbox, application_id=application_id,rawjson=rawjson)
105+
106+
def create_scans_report(self, start_date, end_date=None, scan_type:list = [],
107+
policy_sandbox=None, application_id=None, rawjson=False):
108+
return self.create_report(report_type='scans', last_updated_start_date=start_date,
109+
last_updated_end_date=end_date, scan_type=scan_type,
110+
policy_sandbox=policy_sandbox, application_id=application_id,rawjson=rawjson)
111+
112+
def create_deleted_scans_report(self, start_date, end_date=None, application_id=None,
113+
rawjson=False):
114+
return self.create_report(report_type='deleted_scans', deletion_start_date=start_date,
115+
deletion_end_date=end_date, application_id=application_id,
116+
rawjson=rawjson)
117+
118+
def create_audit_report(self, start_date, end_date=None, audit_action:list=[], target_user_id:int=None,
119+
modifier_user_id:int=None):
120+
return self.create_report(report_type='audit', start_date=start_date, end_date=end_date, audit_action=audit_action,
121+
target_user_id=target_user_id, modifier_user_id=modifier_user_id)
122+
77123
def get_findings(self, guid: UUID):
78124
thestatus, thefindings = self.get(guid=guid,report_type='findings')
79125
return thestatus, thefindings
@@ -84,7 +130,11 @@ def get_scans(self, guid: UUID):
84130

85131
def get_deleted_scans(self, guid: UUID):
86132
thestatus, thescans = self.get(guid=guid,report_type='deletedscans')
87-
return thestatus, thescans
133+
return thestatus, thescans
134+
135+
def get_audits(self, guid: UUID):
136+
thestatus, theaudits = self.get(guid=guid, report_type='audit_logs')
137+
return thestatus, theaudits
88138

89139
def get(self,guid: UUID,report_type='findings'):
90140
# handle multiple scan types

0 commit comments

Comments
 (0)