qualys-mcp 2.1.1__tar.gz → 2.1.2__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.2}/PKG-INFO +1 -1
- {qualys_mcp-2.1.1 → qualys_mcp-2.1.2}/pyproject.toml +1 -1
- {qualys_mcp-2.1.1 → qualys_mcp-2.1.2}/qualys_mcp.py +42 -2
- {qualys_mcp-2.1.1 → qualys_mcp-2.1.2}/.gitignore +0 -0
- {qualys_mcp-2.1.1 → qualys_mcp-2.1.2}/LICENSE +0 -0
- {qualys_mcp-2.1.1 → qualys_mcp-2.1.2}/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.2
|
|
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.2"
|
|
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,46 @@ 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', {})
|
|
136
|
+
lifecycle = os_info.get('lifecycle', {})
|
|
137
|
+
assets.append({
|
|
138
|
+
'assetId': a.get('assetId'),
|
|
139
|
+
'address': a.get('address', ''),
|
|
140
|
+
'dnsName': a.get('dnsHostName', ''),
|
|
141
|
+
'operatingSystem': {
|
|
142
|
+
'osName': f"{os_info.get('name', '')} {os_info.get('version', '')}".strip(),
|
|
143
|
+
'lifecycle': {
|
|
144
|
+
'stage': lifecycle.get('stage', ''),
|
|
145
|
+
'eolDate': lifecycle.get('eolDate', ''),
|
|
146
|
+
'eosDate': lifecycle.get('eosDate', '')
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
})
|
|
150
|
+
return assets
|
|
151
|
+
except Exception as e:
|
|
152
|
+
return []
|
|
153
|
+
|
|
154
|
+
|
|
115
155
|
def get_images(limit=100, severity=None):
|
|
116
156
|
url = f"{GATEWAY_URL}/csapi/v1.3/images?pageSize={limit}"
|
|
117
157
|
if severity:
|
|
@@ -484,7 +524,7 @@ def get_tech_debt(days_until_eol: int = 0) -> dict:
|
|
|
484
524
|
'byOS': []
|
|
485
525
|
}
|
|
486
526
|
|
|
487
|
-
assets =
|
|
527
|
+
assets = get_eol_assets("EOL,EOL/EOS,EOS", 500)
|
|
488
528
|
result['stats']['total'] = len(assets)
|
|
489
529
|
|
|
490
530
|
today = datetime.utcnow().date()
|
|
@@ -523,7 +563,7 @@ def get_tech_debt(days_until_eol: int = 0) -> dict:
|
|
|
523
563
|
os_data[os_name]['eol'] += 1
|
|
524
564
|
if len(result['currentEOL']) < 20:
|
|
525
565
|
result['currentEOL'].append(asset_info)
|
|
526
|
-
elif stage
|
|
566
|
+
elif stage in ('EOS', 'EOL/EOS'):
|
|
527
567
|
result['stats']['currentEOS'] += 1
|
|
528
568
|
os_data[os_name]['eos'] += 1
|
|
529
569
|
if len(result['currentEOS']) < 20:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|