Skip to content

Commit a8ba125

Browse files
Merge pull request #5 from Contrast-Security-OSS/agents
Agents
2 parents 4896380 + 1d36923 commit a8ba125

3 files changed

Lines changed: 536 additions & 1 deletion

File tree

.gitignore

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,9 @@ output.json
55
.DS_Store
66
output.csv
77
apps_and_servers.csv
8-
server_app_metadata.csv
8+
server_app_metadata.csv
9+
applications_output.json
10+
application_languages.csv
11+
servers_output.json
12+
vulnerabilities_output.json
13+
profile_output.json
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
# PowerShell version of get-agents-info.py
2+
3+
function Read-CredsFile {
4+
param(
5+
[string]$FileName = "../.creds"
6+
)
7+
8+
$creds = @{}
9+
10+
if (Test-Path $FileName) {
11+
Get-Content $FileName | ForEach-Object {
12+
$line = $_.Trim()
13+
if ($line -and -not $line.StartsWith("#")) {
14+
$parts = $line.Split("=", 2)
15+
if ($parts.Count -eq 2) {
16+
$creds[$parts[0]] = $parts[1]
17+
}
18+
}
19+
}
20+
} else {
21+
Write-Host "Warning: $FileName file not found. Please input values."
22+
}
23+
24+
return $creds
25+
}
26+
27+
function Get-AgentVersions {
28+
param(
29+
[hashtable]$Headers,
30+
[hashtable]$Params,
31+
[string]$OrgId,
32+
[string]$ContrastUrl
33+
)
34+
35+
$tempHeaders = $Headers.Clone()
36+
$authToken = $tempHeaders["Authorization"]
37+
$tempHeaders["Authorization"] = "Basic $authToken"
38+
39+
$url = "$ContrastUrl/api/ng/$OrgId/agents/versions"
40+
41+
try {
42+
$response = Invoke-RestMethod -Uri $url -Headers $tempHeaders -Method Get
43+
return @{
44+
StatusCode = 200
45+
Data = $response
46+
}
47+
} catch {
48+
return @{
49+
StatusCode = $_.Exception.Response.StatusCode.value__
50+
Data = $null
51+
}
52+
}
53+
}
54+
55+
function Get-Agents {
56+
param(
57+
[hashtable]$Headers,
58+
[string]$OrgId,
59+
[string]$ContrastUrl
60+
)
61+
62+
# Remove /Contrast suffix if present in the base URL for v4 API
63+
$baseUrl = $ContrastUrl.TrimEnd('/') -replace '/Contrast$', ''
64+
$url = "$baseUrl/api/v4/organizations/$OrgId/agents?sort=lastActive,desc&page=0&size=10"
65+
66+
try {
67+
$webRequest = [System.Net.HttpWebRequest]::Create($url)
68+
$webRequest.Method = "GET"
69+
$webRequest.Headers.Add("Accept", $Headers["Accept"])
70+
$webRequest.Headers.Add("Authorization", $Headers["Authorization"])
71+
$webRequest.Headers.Add("API-Key", $Headers["API-Key"])
72+
73+
$webResponse = $webRequest.GetResponse()
74+
$responseStream = $webResponse.GetResponseStream()
75+
$reader = New-Object System.IO.StreamReader($responseStream)
76+
$responseText = $reader.ReadToEnd()
77+
$reader.Close()
78+
$webResponse.Close()
79+
80+
$response = $responseText | ConvertFrom-Json
81+
82+
return @{
83+
StatusCode = 200
84+
Data = $response
85+
}
86+
} catch {
87+
$statusCode = if ($_.Exception.Response) { $_.Exception.Response.StatusCode.value__ } else { 500 }
88+
return @{
89+
StatusCode = $statusCode
90+
Data = $null
91+
}
92+
}
93+
}
94+
95+
function Get-AgentConfig {
96+
param(
97+
[hashtable]$Headers,
98+
[string]$OrgId,
99+
[string]$AgentId,
100+
[string]$AppId,
101+
[string]$ContrastUrl
102+
)
103+
104+
# Remove /Contrast suffix if present in the base URL for v4 API
105+
$baseUrl = $ContrastUrl.TrimEnd('/') -replace '/Contrast$', ''
106+
$url = "$baseUrl/api/v4/organizations/$OrgId/agents/$AgentId/applications/$AppId/effective-config"
107+
108+
try {
109+
$webRequest = [System.Net.HttpWebRequest]::Create($url)
110+
$webRequest.Method = "GET"
111+
$webRequest.Headers.Add("Accept", $Headers["Accept"])
112+
$webRequest.Headers.Add("Authorization", $Headers["Authorization"])
113+
$webRequest.Headers.Add("API-Key", $Headers["API-Key"])
114+
115+
$webResponse = $webRequest.GetResponse()
116+
$responseStream = $webResponse.GetResponseStream()
117+
$reader = New-Object System.IO.StreamReader($responseStream)
118+
$responseText = $reader.ReadToEnd()
119+
$reader.Close()
120+
$webResponse.Close()
121+
122+
$response = $responseText | ConvertFrom-Json
123+
124+
return @{
125+
StatusCode = 200
126+
Data = $response
127+
}
128+
} catch {
129+
$statusCode = if ($_.Exception.Response) { $_.Exception.Response.StatusCode.value__ } else { 500 }
130+
return @{
131+
StatusCode = $statusCode
132+
Data = $null
133+
}
134+
}
135+
}
136+
137+
function Show-ConfigData {
138+
param(
139+
[object]$ConfigData,
140+
[int]$Indent = 4
141+
)
142+
143+
$indentStr = " " * $Indent
144+
145+
if ($ConfigData -is [hashtable] -or $ConfigData -is [PSCustomObject]) {
146+
$props = if ($ConfigData -is [hashtable]) { $ConfigData.Keys } else { $ConfigData.PSObject.Properties.Name }
147+
148+
foreach ($key in $props) {
149+
$value = if ($ConfigData -is [hashtable]) { $ConfigData[$key] } else { $ConfigData.$key }
150+
151+
if ($value -is [hashtable] -or $value -is [PSCustomObject]) {
152+
Write-Host "${indentStr}${key}:"
153+
Show-ConfigData -ConfigData $value -Indent ($Indent + 2)
154+
} elseif ($value -is [array]) {
155+
Write-Host "${indentStr}${key}: $($value | ConvertTo-Json -Compress -Depth 1)"
156+
} else {
157+
Write-Host "${indentStr}${key}: $value"
158+
}
159+
}
160+
}
161+
}
162+
163+
# Main script
164+
$creds = Read-CredsFile
165+
166+
$contrastUrl = $creds["CONTRAST_URL"]
167+
$orgId = $creds["ORG_ID"]
168+
$username = $creds["USERNAME"]
169+
$apiKey = $creds["API_KEY"]
170+
$serviceKey = $creds["SERVICE_KEY"]
171+
172+
# Prompt for values
173+
$urlInput = Read-Host "Enter your Contrast URL (blank will use default '$contrastUrl')"
174+
if ($urlInput.Trim()) {
175+
$contrastUrl = $urlInput
176+
} elseif (-not $contrastUrl) {
177+
while (-not $contrastUrl) {
178+
Write-Host "Contrast URL cannot be blank."
179+
$contrastUrl = Read-Host "Enter your Contrast URL"
180+
}
181+
}
182+
183+
$orgInput = Read-Host "Enter your Organization ID (blank will use default '$orgId')"
184+
if ($orgInput.Trim()) {
185+
$orgId = $orgInput
186+
} elseif (-not $orgId) {
187+
while (-not $orgId) {
188+
Write-Host "Organization ID cannot be blank."
189+
$orgId = Read-Host "Enter your Organization ID"
190+
}
191+
}
192+
193+
$userInput = Read-Host "Enter your username (blank will use default '$username')"
194+
if ($userInput.Trim()) {
195+
$username = $userInput
196+
} elseif (-not $username) {
197+
while (-not $username) {
198+
Write-Host "Username cannot be blank."
199+
$username = Read-Host "Enter your username"
200+
}
201+
}
202+
203+
$apiKeyInput = Read-Host "Enter your API key (blank will use default '****************************')"
204+
if ($apiKeyInput.Trim()) {
205+
$apiKey = $apiKeyInput
206+
} elseif (-not $apiKey) {
207+
while (-not $apiKey) {
208+
Write-Host "API key cannot be blank."
209+
$apiKey = Read-Host "Enter your API key"
210+
}
211+
}
212+
213+
$serviceKeyInput = Read-Host "Enter your service key (blank will use default '************')"
214+
if ($serviceKeyInput.Trim()) {
215+
$serviceKey = $serviceKeyInput
216+
} elseif (-not $serviceKey) {
217+
while (-not $serviceKey) {
218+
Write-Host "Service key cannot be blank."
219+
$serviceKey = Read-Host "Enter your service key"
220+
}
221+
}
222+
223+
# Create authorization header
224+
$authString = "${username}:${serviceKey}"
225+
$authBytes = [System.Text.Encoding]::UTF8.GetBytes($authString)
226+
$authB64 = [Convert]::ToBase64String($authBytes)
227+
228+
$headers = @{
229+
"Accept" = "application/json"
230+
"Authorization" = $authB64
231+
"API-Key" = $apiKey
232+
}
233+
234+
$params = @{
235+
"expand" = @("apps", "vulns")
236+
}
237+
238+
# Get agent versions
239+
Write-Host ""
240+
$versionResponse = Get-AgentVersions -Headers $headers -Params $params -OrgId $orgId -ContrastUrl $contrastUrl
241+
Write-Host "Agent Versions response status code: $($versionResponse.StatusCode)"
242+
if ($versionResponse.StatusCode -eq 200) {
243+
Write-Host ($versionResponse.Data | ConvertTo-Json -Depth 10)
244+
}
245+
246+
# Get agents
247+
$agentResponse = Get-Agents -Headers $headers -OrgId $orgId -ContrastUrl $contrastUrl
248+
Write-Host "Agents response status code: $($agentResponse.StatusCode)"
249+
250+
if ($agentResponse.StatusCode -eq 200) {
251+
$data = $agentResponse.Data
252+
253+
# Save to JSON file
254+
$data | ConvertTo-Json -Depth 100 | Out-File "output.json"
255+
Write-Host "Applications response saved to output.json"
256+
257+
if ($data.content) {
258+
$agents = $data.content
259+
Write-Host "`nFound $($agents.Count) agents:`n"
260+
261+
foreach ($agent in $agents) {
262+
$fullAgentId = $agent.id
263+
264+
# Split the agent ID - format is "app_id:agent_id"
265+
if ($fullAgentId -match ':') {
266+
$idParts = $fullAgentId.Split(':', 2)
267+
$appId = $idParts[0]
268+
$agentId = $idParts[1]
269+
} else {
270+
Write-Host "Warning: Could not parse agent ID: $fullAgentId"
271+
continue
272+
}
273+
274+
Write-Host "Agent ID: $fullAgentId"
275+
Write-Host " App ID: $appId"
276+
Write-Host " Agent Instance ID: $agentId"
277+
Write-Host " Application: $($agent.applicationName)"
278+
Write-Host " Server: $($agent.serverName)"
279+
Write-Host " Language: $($agent.language)"
280+
Write-Host " Version: $($agent.displayVersion)"
281+
Write-Host " Status: $($agent.status)"
282+
Write-Host " Environment: $($agent.environment)"
283+
Write-Host " Last Active: $($agent.lastActive)"
284+
Write-Host " Hostname: $($agent.hostname)"
285+
286+
# Add server inventory details if available
287+
if ($agent.serverInventory) {
288+
$inventory = $agent.serverInventory
289+
Write-Host " OS: $($inventory.operatingSystem)"
290+
Write-Host " Runtime: $($inventory.runtimeVersion)"
291+
Write-Host " Docker: $($inventory.isDocker), Kubernetes: $($inventory.isKubernetes)"
292+
}
293+
294+
# Get agent configuration
295+
$configResponse = Get-AgentConfig -Headers $headers -OrgId $orgId -AgentId $agentId -AppId $appId -ContrastUrl $contrastUrl
296+
if ($configResponse.StatusCode -eq 200) {
297+
Write-Host " Config retrieved successfully"
298+
Write-Host " Effective Config:"
299+
300+
$configData = $configResponse.Data
301+
Show-ConfigData -ConfigData $configData -Indent 4
302+
} else {
303+
Write-Host " Config retrieval failed: $($configResponse.StatusCode)"
304+
}
305+
306+
Write-Host "" # Blank line between agents
307+
}
308+
} else {
309+
Write-Host "No agents found in response."
310+
}
311+
}

0 commit comments

Comments
 (0)