This repository was archived by the owner on Feb 18, 2026. It is now read-only.
Repository navigation
Expand file tree
/
Copy pathport-kics.py
More file actions
163 lines (132 loc) · 5.06 KB
/
Copy pathport-kics.py
File metadata and controls
163 lines (132 loc) · 5.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import json
import os
import sys
import requests
import time
base_delay = 1 # 1 second
max_retries = 5
max_delay = 32
API_URL = 'https://api.getport.io/v1'
def make_api_request(query, headers):
print(query)
response = requests.post(f'{API_URL}/blueprints/kicsScan/entities?upsert=true&merge=true&create_missing_related_entities=true', json=query, headers=headers)
pass
def retry_with_exponential_backoff(query, headers):
attempt = 0
delay = base_delay
while attempt < max_retries:
try:
result = make_api_request(query, headers)
return result
except Exception as e:
attempt += 1
if attempt >= max_retries:
raise e
sleep_time = min(delay * (2 ** attempt), max_delay)
print(f"Attempt {attempt} failed. Retrying in {sleep_time:.2f} seconds...")
time.sleep(sleep_time)
raise Exception("All retry attempts failed.")
def get_access_token():
"""
Retrieves an access token from Port's API.
Returns:
str: The access token.
"""
CLIENT_ID = os.getenv("PORT_CLIENT_ID")
CLIENT_SECRET = os.getenv("PORT_CLIENT_SECRET")
credentials = {'clientId': CLIENT_ID, 'clientSecret': CLIENT_SECRET}
token_response = requests.post(f'{API_URL}/auth/access_token', json=credentials)
print(token_response.json())
return token_response.json()['accessToken']
def parse_kics_results(file_path, repo_name):
"""
Parses KICS results.json file to extract findings and files.
Args:
file_path (str): Path to the results.json file that KICS creates.
Returns:
dict: A single entity with all the findings.
Future consideration: Parse the results.json file to get the findings per file and
send to Port as separate entities + file entities.
Data structure:
entity:
mappings:
identifier: <file>.query_id
title: <file>.query_name
blueprint: '"kicsScan"'
properties:
category: <file>.category
cloud_provider: <file>.cloud_provider
description: <file>.description
files: <file>.files
severity: <file>.severity
platform: <file>.platform
url: <file>.url
"""
entities = []
try:
with open(file_path, "r", encoding="utf-8") as file:
content = json.load(file)
for query in content["queries"]:
entities.append({
"identifier": query["query_id"],
"title": query["query_name"],
"properties": {
"category": query["category"],
"cloud_provider": query["cloud_provider"],
"description": query["description"],
"files": query["files"],
"severity": query["severity"],
"platform": query["platform"],
"url": "https://github.com/" + repo_name
},
"relations": {
"service": repo_name
}
})
except FileNotFoundError:
print(f"Error: KICS result file not found at {file_path}")
except Exception as e:
print(f"Error parsing file: {e}")
return entities
def create_service_entity(repo_name, query_ids):
"""
Creates a service entity and connects it to its dependencies.
Args:
repo_name (str): The repository name (without the organization).
dependencies (list): A list of dependency identifiers.
Returns:
dict: A service entity formatted for Port's BULK_UPSERT.
"""
return {
"identifier": repo_name,
"title": repo_name,
"properties": {
"url": "https;//github.com/" + repo_name
},
}
def main():
kics_results = sys.argv[1]
if not kics_results:
raise ValueError("Must include the path to the KICS results file as an argument.")
# get the access token
access_token = get_access_token()
headers = {
'Authorization': f'Bearer {access_token}'
}
# Extract the repository name from GITHUB_REPOSITORY
repo_name = os.getenv("GITHUB_REPOSITORY", "JimCodified/default-repo")
# Parse the KICS results file
results = parse_kics_results(kics_results, repo_name)
# Collect query identifiers for relations
query_ids = [q["identifier"] for q in results]
# Create the service entity
service_entity = create_service_entity(repo_name, query_ids)
# upsert svc entity - Note the ?upsert=true&merge=true query parameters
response = requests.post(f'{API_URL}/blueprints/service/entities?upsert=true&merge=true&create_missing_related_entities=true', json=service_entity, headers=headers)
# upsert the queries with some API rate limit handling
for query in results:
retry_with_exponential_backoff(query, headers)
# Output the result in JSON format for Port
#print(json.dumps(all_entities, indent=2))
if __name__ == "__main__":
main()