qualys-mcp 2.1.1__tar.gz → 2.1.3__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.
- {qualys_mcp-2.1.1 → qualys_mcp-2.1.3}/PKG-INFO +1 -1
- {qualys_mcp-2.1.1 → qualys_mcp-2.1.3}/pyproject.toml +1 -1
- {qualys_mcp-2.1.1 → qualys_mcp-2.1.3}/qualys_mcp.py +45 -2
- {qualys_mcp-2.1.1 → qualys_mcp-2.1.3}/.gitignore +0 -0
- {qualys_mcp-2.1.1 → qualys_mcp-2.1.3}/LICENSE +0 -0
- {qualys_mcp-2.1.1 → qualys_mcp-2.1.3}/README.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qualys-mcp
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.3
|
|
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.
|
|
7
|
+
version = "2.1.3"
|
|
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"
|
|
@@ -112,6 +112,49 @@ def get_assets(limit=100, qql=None):
|
|
|
112
112
|
return []
|
|
113
113
|
|
|
114
114
|
|
|
115
|
+
def get_eol_assets(stage_filter="EOL,EOL/EOS", limit=500):
|
|
116
|
+
url = f"{GATEWAY_URL}/rest/2.0/search/am/asset?pageSize={limit}"
|
|
117
|
+
token = get_bearer_token()
|
|
118
|
+
|
|
119
|
+
filter_body = json.dumps({
|
|
120
|
+
"filters": [
|
|
121
|
+
{"field": "operatingSystem.lifecycle.stage", "operator": "IN", "value": stage_filter}
|
|
122
|
+
]
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
req = Request(url, data=filter_body.encode(), method='POST')
|
|
126
|
+
req.add_header('Authorization', f'Bearer {token}' if token else f'Basic {BASIC_AUTH}')
|
|
127
|
+
req.add_header('Content-Type', 'application/json')
|
|
128
|
+
req.add_header('X-Requested-With', 'qualys-mcp')
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
with urlopen(req, timeout=60) as resp:
|
|
132
|
+
data = json.loads(resp.read())
|
|
133
|
+
assets = []
|
|
134
|
+
for a in data.get('assetListData', {}).get('asset', []):
|
|
135
|
+
os_info = a.get('operatingSystem', {}) or {}
|
|
136
|
+
lifecycle = os_info.get('lifecycle', {}) or {}
|
|
137
|
+
os_name = os_info.get('osName', '') or os_info.get('fullName', '') or ''
|
|
138
|
+
if os_info.get('version'):
|
|
139
|
+
os_name = f"{os_name} {os_info.get('version', '')}".strip()
|
|
140
|
+
assets.append({
|
|
141
|
+
'assetId': a.get('assetId'),
|
|
142
|
+
'address': a.get('address', ''),
|
|
143
|
+
'dnsName': a.get('dnsHostName', ''),
|
|
144
|
+
'operatingSystem': {
|
|
145
|
+
'osName': os_name,
|
|
146
|
+
'lifecycle': {
|
|
147
|
+
'stage': lifecycle.get('stage', ''),
|
|
148
|
+
'eolDate': lifecycle.get('eolDate', ''),
|
|
149
|
+
'eosDate': lifecycle.get('eosDate', '')
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
return assets
|
|
154
|
+
except Exception as e:
|
|
155
|
+
return []
|
|
156
|
+
|
|
157
|
+
|
|
115
158
|
def get_images(limit=100, severity=None):
|
|
116
159
|
url = f"{GATEWAY_URL}/csapi/v1.3/images?pageSize={limit}"
|
|
117
160
|
if severity:
|
|
@@ -484,7 +527,7 @@ def get_tech_debt(days_until_eol: int = 0) -> dict:
|
|
|
484
527
|
'byOS': []
|
|
485
528
|
}
|
|
486
529
|
|
|
487
|
-
assets =
|
|
530
|
+
assets = get_eol_assets("EOL,EOL/EOS,EOS", 500)
|
|
488
531
|
result['stats']['total'] = len(assets)
|
|
489
532
|
|
|
490
533
|
today = datetime.utcnow().date()
|
|
@@ -523,7 +566,7 @@ def get_tech_debt(days_until_eol: int = 0) -> dict:
|
|
|
523
566
|
os_data[os_name]['eol'] += 1
|
|
524
567
|
if len(result['currentEOL']) < 20:
|
|
525
568
|
result['currentEOL'].append(asset_info)
|
|
526
|
-
elif stage
|
|
569
|
+
elif stage in ('EOS', 'EOL/EOS'):
|
|
527
570
|
result['stats']['currentEOS'] += 1
|
|
528
571
|
os_data[os_name]['eos'] += 1
|
|
529
572
|
if len(result['currentEOS']) < 20:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|