Cycode supports exporting data through a variety of different methods including CSV, JSON, Webhook, and using APIs. When focusing on APIs, a common use case is to take a snapshot of your security posture and ingest that data into other tools, often homegrown tools built by security teams. Since Cycode is API driven, we have a wide selection of APIs to pull violation data and their assets such as repositories, labels, organizations, commits, and user activity.
In this blog, we’re going to focus on:
- Create custom query using the Risk Intelligence Graph (RIG)
- Customize it through the UI
- Download the results through our API
The advantage of this approach is you can customize the report to combine assets from multiple tools and then use one API endpoint to pull this information down. And because we’re using the RIG, query customizing is very easy.
Problem to Solve with the RIG API
The example below shows High/Critical container vulnerabilities that we’ve ingested from Prisma Cloud. Using the Cycode Complete ASPM platform, we’ll also relate these vulnerabilities to other assets like the container image it came from, the Dockerfile it was built from, and finally the developers who committed code. This gives us a complete view of your vulnerabilities and can help you trace back to the right team that should be notified.
While we can create workflows to automatically send this report through Slack, Teams, or Email, we’re going to look at the example of pulling this data through our API instead.
RIG APIs
The RIG API is useful because we can manipulate RIG queries in flexible ways. The RIG can correlate data from multiple tools and allows you to customize the report. For example in the above query, we are taking information from vulnerabilities that we’ve found in the container image repository, matching that to the container image, to the Dockerfile in the SCM repository, and finally tracking that back to the users who committed code. This shows a report that is taking information from your container image repo and SCM repo. These reports can be expanded to other SDLC tools, including your build pipelines and cloud infrastructure.
Lets Get Started
Check out the code below to download the RIG query. It takes a query file that you create next. Save this file as “download-rig-query.py”.
import requests, sys, logging, json, os, time from collections import defaultdict headers = {} def get_token(client_id, client_secret): # print('client id and secret: ' +client_id+' '+client_secret) #debug Uncomment to see client id and secret print('Retrieving JWT Token...') token = '' auth_headers = {"Content-Type": "application/json", "Accept": "application/json"} contents = {"clientId": client_id, "secret": client_secret} contents = json.dumps(contents) r = requests.post(f"https://{cycode_api_url}/api/v1/auth/api-token", data=contents, headers=auth_headers, timeout=3*60) response = r.json() if r.status_code == 200: token = response['token'] print("New JWT token was obtained.") # print(f'response: {response}') # debug Uncomment to see token in response else: logging.info("New JWT token could not be obtained. Exiting...") sys.exit(1) return token def assign_token(): return os.environ['CYCODE_TOKEN'] def refresh_headers(): headers['Authorization'] = "Bearer " + get_token(os.environ['CYCODE_CLIENT_ID'], os.environ['CYCODE_CLIENT_SECRET']) # download RIG query using query_file and download report def create_rig_report(query_file): # query is a filename if ".json" in query_file: # get data for query from file with open(query_file, 'r') as file: raw_query = json.load(file) # query contents should be passed directly else: print('Query is not a JSON file, exiting.') sys.exit(1) # modify query file so that RIG API can understand this as a query = { "output_format": "JSON", "graph_query_entity": raw_query, "parameters": { "name": "query-modified" } } # -------------------------------- # Create Standalone report # -------------------------------- print("Creating Standalone report") url = f"https://{cycode_api_url}/report/api/v2/report/standalone-execute" # create report response = requests.post(url, headers=headers, json=query) # get report id report_id = response.json().get('id') print(f" - Report ID: {report_id}") # get executiuon id execution_id = response.json().get('report_executions')[0].get('id') # -------------------------------- # Report Status # -------------------------------- response = requests.get(url, headers=headers) url = f"https://{cycode_api_url}/report/api/v2/report/executions?executions_ids[0]={execution_id}&include_orphan_executions=true" status = 'Pending' report_path = '' while (status != 'Completed'): response = requests.get(url, headers=headers) # print(f" - Response: {response.json()}") status = response.json()[0].get('status') if status == 'Failed': print('Report failed') print(response.json()) sys.exit(1) print(f" - - Report status: {status}") time.sleep(40) report_path = response.json()[0].get('storage_details').get('path') # -------------------------------- # Download Report # -------------------------------- print('Downloading report') url = f"https://{cycode_api_url}/files/api/v1/file/reports/{report_path}" response = requests.get(url, headers=headers) return response.json() # write data to filename as JSON file def write_json_file(data, filename): if filename == "": filename = "report_output" filename = f'{filename}.json' with open(filename, 'w') as file: file.write(json.dumps(data)) ## main function if __name__=="__main__": page_index = 1 repo_url_template = "https://{cycode_app_url}/api/repositories?pageIndex={idx}&limit={limit}" cycode_app_url = "app.cycode.com" cycode_api_url = "api." + cycode_app_url.split('.',1)[1] # get arguments (expecting 1 arg that is query file) if len(sys.argv) != 2: print("Expected 1 argument that is the filename of query") sys.exit(1) query_file = sys.argv[1] token = get_token(os.environ['CYCODE_CLIENT_ID'], os.environ['CYCODE_CLIENT_SECRET']) headers = { "Content-Type": "application/json", "Accept": "application/json", "Authorization": "Bearer " + token } # store RIG query response in variable results results = create_rig_report(query_file) # write results to a JSON file write_json_file(results, "RESULTS")
The next step is opening the RIG in Cycode and creating your own query. We have a variety of queries out of the box in the Library section, or you can create your own. Make sure to add the fields that you want by selecting the Columns button. For this blog, I’m using my Prisma Cloud example shown above.
After you’re done, check out the two squares icon in the top right corner. Hover over that icon and select the Clipboard icon. Save this output into a file named “query-file.json”. This file defines the query as you’ve created it in the RIG.
Generate API Token
Inside the Cycode platform, go to your user settings and select Access Tokens.
Click the “Create New” button. After providing a description of the token, you are presented with a client ID and client secret. Copy both of those values into a text file locally, clearly labeling them as “CLIENT ID” and “CLIENT SECRET”. You need both a client ID and secret to run the script in the next step. Any consumers of this script will need their own client ID and secret.
Run Script
After copying the CLIENT_ID and CLIENT_SECRET, replace the below variables with your values. This script takes in a JSON file as the query, (“query-file.json”) and then outputs the results in another JSON file as the report. Note that the filename “query-file.json” should be the output from the RIG outlined earlier.
export CYCODE_CLIENT_ID="7fde2f55-4b72-4495-8814-eeb387ea630c" && export CYCODE_CLIENT_SECRET="505dtdc8-3bcd-969b-9f97-7dd1c1578ab2" python3 download-rig-query.py query-file.json
Results
After the script runs, it creates a JSON file titled “RESULTS.json”. This file outputs the results from your RIG query. In my example, you can see the container image vulnerability, container image, Dockerfile, commit, and user from the graph above.
This is a specific example, but you can create your own queries and use this API in the same manner. To change your query, you just need to change the query-file.json contents. Remember to add columns you want to see in the RIG UI to ensure they are included in the results file.
Using the RIG API can help you in multiple scenarios. Reporting on specific scenarios and ingesting those results in homegrown or other tools is a common use case. Pulling complex reports that span multiple SDLC tools is another popular reason. Or if you’re interested in a GitOps use case, check out this blog Shifting Smart with GitOps, which explains how to pull information from the RIG and package that into a GitHub Actions workflow.
About Cycode
By leveraging Cycode’s advanced capabilities, including our RIG API, organizations can enhance their security posture, mitigate risks, and safeguard their most valuable assets in an ever-evolving threat landscape.
Cycode is the leading Application Security Posture Management (ASPM) providing peace of mind to its customers. Its Complete ASPM platform scales and standardizes developer security without slowing down the business to deliver safe code, faster.
The platform can replace existing application security testing tools or integrate them while providing cyber resiliency through unmatched visibility, risk driven prioritization and just in-time remediation of code vulnerabilities at scale. Cycode’s Risk Intelligence Graph (RIG), the ‘brain’ behind the platform, provides traceability across the entire SDLC through natural language.
If you’re ready to learn more about Cycode, book a demo now.