qualys-mcp 2.1.4__tar.gz → 2.1.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qualys-mcp
3
- Version: 2.1.4
3
+ Version: 2.1.6
4
4
  Summary: MCP server for Qualys security APIs - natural language interaction with vulnerability, asset, and cloud security data
5
5
  Project-URL: Homepage, https://github.com/nelssec/qualys-mcp
6
6
  Project-URL: Repository, https://github.com/nelssec/qualys-mcp
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "qualys-mcp"
7
- version = "2.1.4"
7
+ version = "2.1.6"
8
8
  description = "MCP server for Qualys security APIs - natural language interaction with vulnerability, asset, and cloud security data"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -42,7 +42,7 @@ def get_bearer_token():
42
42
  return None
43
43
 
44
44
 
45
- def api_get(url, gateway=False):
45
+ def api_get(url, gateway=False, timeout=30):
46
46
  req = Request(url)
47
47
  if gateway:
48
48
  token = get_bearer_token()
@@ -51,7 +51,7 @@ def api_get(url, gateway=False):
51
51
  req.add_header('Authorization', f'Basic {BASIC_AUTH}')
52
52
  req.add_header('X-Requested-With', 'qualys-mcp')
53
53
  try:
54
- with urlopen(req, timeout=60) as resp:
54
+ with urlopen(req, timeout=timeout) as resp:
55
55
  return resp.read()
56
56
  except:
57
57
  return None
@@ -381,34 +381,49 @@ def get_security_posture() -> dict:
381
381
  health = 100
382
382
  result = {'healthScore': 0, 'assets': {'total': 0, 'highRisk': 0},
383
383
  'vulns': {'critical': 0, 'high': 0}, 'containers': {'total': 0, 'atRisk': 0},
384
- 'cloud': {'accounts': 0, 'failedControls': 0}}
384
+ 'cloud': {'accounts': 0, 'failedControls': 0}, 'errors': []}
385
385
 
386
- assets = get_assets(500)
387
- result['assets']['total'] = len(assets)
388
- result['assets']['highRisk'] = len([a for a in assets if a.get('assetRiskScore', 0) >= 700])
389
- if assets:
390
- health -= int(result['assets']['highRisk'] / len(assets) * 50)
391
-
392
- result['vulns']['critical'] = len(get_detections(5, 200))
393
- result['vulns']['high'] = len(get_detections(4, 200))
394
- if result['vulns']['critical'] > 50:
395
- health -= 20
396
- elif result['vulns']['critical'] > 10:
397
- health -= 10
398
-
399
- imgs = get_images(500)
400
- result['containers']['total'] = len(imgs)
401
- vuln_ids = {i.get('imageId') for i in get_images(100, 5)}
402
- result['containers']['atRisk'] = len([c for c in get_containers(500) if c.get('imageId') in vuln_ids])
386
+ try:
387
+ assets = get_assets(100)
388
+ result['assets']['total'] = len(assets)
389
+ result['assets']['highRisk'] = len([a for a in assets if a.get('assetRiskScore', 0) >= 700])
390
+ if assets:
391
+ health -= int(result['assets']['highRisk'] / len(assets) * 50)
392
+ except:
393
+ result['errors'].append('assets')
403
394
 
404
- for p in ['aws', 'azure', 'gcp']:
405
- conns = get_connectors(p, 20)
406
- result['cloud']['accounts'] += len(conns)
407
- if conns:
408
- acc = conns[0].get('awsAccountId') or conns[0].get('azureSubscriptionId') or conns[0].get('gcpProjectId')
409
- if acc:
410
- result['cloud']['failedControls'] += len([e for e in get_evaluations(acc, p, 500) if e.get('result') in ['FAIL', 'FAILED']])
395
+ try:
396
+ result['vulns']['critical'] = len(get_detections(5, 100))
397
+ result['vulns']['high'] = len(get_detections(4, 100))
398
+ if result['vulns']['critical'] > 50:
399
+ health -= 20
400
+ elif result['vulns']['critical'] > 10:
401
+ health -= 10
402
+ except:
403
+ result['errors'].append('vulns')
404
+
405
+ try:
406
+ imgs = get_images(100)
407
+ result['containers']['total'] = len(imgs)
408
+ vuln_ids = {i.get('imageId') for i in get_images(50, 5)}
409
+ result['containers']['atRisk'] = len([c for c in get_containers(100) if c.get('imageId') in vuln_ids])
410
+ except:
411
+ result['errors'].append('containers')
412
+
413
+ try:
414
+ for p in ['aws', 'azure', 'gcp']:
415
+ conns = get_connectors(p, 5)
416
+ if conns:
417
+ result['cloud']['accounts'] += len(conns)
418
+ acc = conns[0].get('awsAccountId') or conns[0].get('azureSubscriptionId') or conns[0].get('gcpProjectId')
419
+ if acc:
420
+ evals = get_evaluations(acc, p, 100)
421
+ result['cloud']['failedControls'] += len([e for e in evals if e.get('result') in ['FAIL', 'FAILED']])
422
+ except:
423
+ result['errors'].append('cloud')
411
424
 
425
+ if not result['errors']:
426
+ del result['errors']
412
427
  result['healthScore'] = max(0, health)
413
428
  return result
414
429
 
File without changes
File without changes
File without changes