blackshield-sdk 0.2.2__tar.gz → 0.2.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.
Files changed (115) hide show
  1. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/.gitignore +3 -1
  2. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/PKG-INFO +8 -1
  3. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/README.md +7 -0
  4. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/_async_client.py +14 -0
  5. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/_client.py +14 -0
  6. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/_version.py +1 -1
  7. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/__init__.py +6 -0
  8. blackshield_sdk-0.2.3/src/blackshield/resources/appliances.py +54 -0
  9. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/identity.py +44 -0
  10. blackshield_sdk-0.2.3/src/blackshield/resources/rules.py +220 -0
  11. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/security_agents.py +62 -6
  12. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/siem.py +71 -3
  13. blackshield_sdk-0.2.3/src/blackshield/types/appliances.py +23 -0
  14. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/identity.py +37 -0
  15. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/ingestion.py +9 -1
  16. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/security_agents.py +44 -2
  17. blackshield_sdk-0.2.3/src/blackshield/types/siem.py +98 -0
  18. blackshield_sdk-0.2.3/tests/test_alerts.py +171 -0
  19. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/tests/test_api_keys.py +38 -4
  20. blackshield_sdk-0.2.3/tests/test_appliances_and_siem_sdk.py +271 -0
  21. blackshield_sdk-0.2.3/tests/test_audit.py +74 -0
  22. blackshield_sdk-0.2.3/tests/test_base_client_errors.py +89 -0
  23. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/tests/test_client.py +1 -1
  24. blackshield_sdk-0.2.3/tests/test_compliance.py +246 -0
  25. blackshield_sdk-0.2.3/tests/test_evidence_siem_runbooks.py +506 -0
  26. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/tests/test_findings.py +87 -6
  27. blackshield_sdk-0.2.3/tests/test_identity.py +159 -0
  28. blackshield_sdk-0.2.3/tests/test_ingestion.py +252 -0
  29. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/tests/test_mcp.py +76 -5
  30. blackshield_sdk-0.2.3/tests/test_policies.py +273 -0
  31. blackshield_sdk-0.2.3/tests/test_remediation.py +177 -0
  32. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/tests/test_reports.py +62 -4
  33. blackshield_sdk-0.2.3/tests/test_rules.py +292 -0
  34. blackshield_sdk-0.2.3/tests/test_security_agents.py +443 -0
  35. blackshield_sdk-0.2.3/tests/test_services_and_incidents.py +249 -0
  36. blackshield_sdk-0.2.2/src/blackshield/types/siem.py +0 -45
  37. blackshield_sdk-0.2.2/tests/test_alerts.py +0 -49
  38. blackshield_sdk-0.2.2/tests/test_audit.py +0 -48
  39. blackshield_sdk-0.2.2/tests/test_compliance.py +0 -116
  40. blackshield_sdk-0.2.2/tests/test_evidence_siem_runbooks.py +0 -252
  41. blackshield_sdk-0.2.2/tests/test_identity.py +0 -73
  42. blackshield_sdk-0.2.2/tests/test_ingestion.py +0 -195
  43. blackshield_sdk-0.2.2/tests/test_policies.py +0 -88
  44. blackshield_sdk-0.2.2/tests/test_remediation.py +0 -67
  45. blackshield_sdk-0.2.2/tests/test_security_agents.py +0 -97
  46. blackshield_sdk-0.2.2/tests/test_services_and_incidents.py +0 -108
  47. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/pyproject.toml +0 -0
  48. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/__init__.py +0 -0
  49. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/_base_client.py +0 -0
  50. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/_exceptions.py +0 -0
  51. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/_types.py +0 -0
  52. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/mcp.py +0 -0
  53. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/ai_assets.py +0 -0
  54. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/ai_gateway.py +0 -0
  55. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/ai_integrations.py +0 -0
  56. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/alerts.py +0 -0
  57. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/api_keys.py +0 -0
  58. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/assertions.py +0 -0
  59. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/audit.py +0 -0
  60. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/compliance.py +0 -0
  61. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/connectors.py +0 -0
  62. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/customer_notifications.py +0 -0
  63. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/dashboard.py +0 -0
  64. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/dast.py +0 -0
  65. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/easm.py +0 -0
  66. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/evidence.py +0 -0
  67. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/findings.py +0 -0
  68. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/incidents.py +0 -0
  69. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/ingestion.py +0 -0
  70. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/oauth_apps.py +0 -0
  71. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/policies.py +0 -0
  72. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/remediation.py +0 -0
  73. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/reports.py +0 -0
  74. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/runbooks.py +0 -0
  75. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/security_reviews.py +0 -0
  76. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/services.py +0 -0
  77. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/threat_intel.py +0 -0
  78. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/resources/threat_modeling.py +0 -0
  79. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/__init__.py +0 -0
  80. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/ai_assets.py +0 -0
  81. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/ai_gateway.py +0 -0
  82. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/ai_integrations.py +0 -0
  83. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/alerts.py +0 -0
  84. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/api_keys.py +0 -0
  85. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/assertions.py +0 -0
  86. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/audit.py +0 -0
  87. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/compliance.py +0 -0
  88. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/connectors.py +0 -0
  89. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/customer_notifications.py +0 -0
  90. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/dashboard.py +0 -0
  91. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/dast.py +0 -0
  92. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/easm.py +0 -0
  93. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/evidence.py +0 -0
  94. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/findings.py +0 -0
  95. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/incidents.py +0 -0
  96. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/oauth_apps.py +0 -0
  97. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/policies.py +0 -0
  98. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/remediation.py +0 -0
  99. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/reports.py +0 -0
  100. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/runbooks.py +0 -0
  101. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/security_reviews.py +0 -0
  102. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/services.py +0 -0
  103. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/threat_intel.py +0 -0
  104. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/src/blackshield/types/threat_modeling.py +0 -0
  105. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/tests/conftest.py +0 -0
  106. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/tests/test_ai_gateway.py +0 -0
  107. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/tests/test_ai_governance_sdk.py +0 -0
  108. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/tests/test_assertions_dast_sdk.py +0 -0
  109. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/tests/test_connectors.py +0 -0
  110. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/tests/test_dashboard.py +0 -0
  111. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/tests/test_easm.py +0 -0
  112. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/tests/test_notifications_threat_modeling_sdk.py +0 -0
  113. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/tests/test_request_models.py +0 -0
  114. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/tests/test_security_reviews.py +0 -0
  115. {blackshield_sdk-0.2.2 → blackshield_sdk-0.2.3}/tests/test_threat_intel.py +0 -0
@@ -52,4 +52,6 @@ coverage.xml
52
52
  .claude/worktrees/
53
53
 
54
54
  Blackshield features - Human Review - Features - v1.csv
55
- .coverage.*
55
+ .coverage.*
56
+ # Local ingestion failure evidence is private runtime data.
57
+ .ingestion-quarantine/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: blackshield-sdk
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Official Python SDK for the BlackShield Security Platform
5
5
  Project-URL: Homepage, https://blackshield.chaplau.com
6
6
  Project-URL: Documentation, https://blackshield.chaplau.com/en/docs
@@ -100,6 +100,13 @@ asyncio.run(main())
100
100
  | `client.oauth_apps` | OAuth app and grant governance | `client.oauth_apps.grants(status="pending_review")` |
101
101
  | `client.threat_modeling` | Hosted threat-model sessions & exports | `client.threat_modeling.create_session(title="Review")` |
102
102
 
103
+ The 0.2.3 release candidate adds appliance inventory and policy operations,
104
+ identity sessions, agent operations, SIEM entities and statistics, and ingestion
105
+ submission responses that accept either `id` or `job_id`. These operations have
106
+ matching synchronous and asynchronous clients. Publish and verify this candidate
107
+ before updating a lab's SDK lock; an existing 0.2.2 installation does not include
108
+ these additions.
109
+
103
110
  Version 0.2.2 preserves the 0.2.0 surface and accepts both the legacy `job_id`
104
111
  and current `id` ingestion job status fields. It also includes the 0.2.1 durable
105
112
  EASM verification outcomes, explicit challenge rotation, and synchronous/asynchronous
@@ -70,6 +70,13 @@ asyncio.run(main())
70
70
  | `client.oauth_apps` | OAuth app and grant governance | `client.oauth_apps.grants(status="pending_review")` |
71
71
  | `client.threat_modeling` | Hosted threat-model sessions & exports | `client.threat_modeling.create_session(title="Review")` |
72
72
 
73
+ The 0.2.3 release candidate adds appliance inventory and policy operations,
74
+ identity sessions, agent operations, SIEM entities and statistics, and ingestion
75
+ submission responses that accept either `id` or `job_id`. These operations have
76
+ matching synchronous and asynchronous clients. Publish and verify this candidate
77
+ before updating a lab's SDK lock; an existing 0.2.2 installation does not include
78
+ these additions.
79
+
73
80
  Version 0.2.2 preserves the 0.2.0 surface and accepts both the legacy `job_id`
74
81
  and current `id` ingestion job status fields. It also includes the 0.2.1 durable
75
82
  EASM verification outcomes, explicit challenge rotation, and synchronous/asynchronous
@@ -8,6 +8,7 @@ from blackshield.resources.ai_gateway import AsyncAiGatewayResource
8
8
  from blackshield.resources.ai_integrations import AsyncAiIntegrationsResource
9
9
  from blackshield.resources.alerts import AsyncAlertsResource
10
10
  from blackshield.resources.api_keys import AsyncApiKeysResource
11
+ from blackshield.resources.appliances import AsyncAppliancesResource
11
12
  from blackshield.resources.assertions import AsyncAssertionsResource
12
13
  from blackshield.resources.audit import AsyncAuditResource
13
14
  from blackshield.resources.compliance import AsyncComplianceResource
@@ -25,6 +26,7 @@ from blackshield.resources.oauth_apps import AsyncOAuthAppsResource
25
26
  from blackshield.resources.policies import AsyncPoliciesResource
26
27
  from blackshield.resources.remediation import AsyncRemediationResource
27
28
  from blackshield.resources.reports import AsyncReportsResource
29
+ from blackshield.resources.rules import AsyncRulesResource
28
30
  from blackshield.resources.runbooks import AsyncRunbooksResource
29
31
  from blackshield.resources.security_agents import AsyncSecurityAgentsResource
30
32
  from blackshield.resources.security_reviews import AsyncSecurityReviewsResource
@@ -82,6 +84,8 @@ class AsyncBlackShield:
82
84
  self._oauth_apps = AsyncOAuthAppsResource(self._http)
83
85
  self._customer_notifications = AsyncCustomerNotificationsResource(self._http)
84
86
  self._threat_modeling = AsyncThreatModelingResource(self._http)
87
+ self._rules = AsyncRulesResource(self._http)
88
+ self._appliances = AsyncAppliancesResource(self._http)
85
89
 
86
90
  @property
87
91
  def findings(self) -> AsyncFindingsResource:
@@ -203,6 +207,16 @@ class AsyncBlackShield:
203
207
  def threat_modeling(self) -> AsyncThreatModelingResource:
204
208
  return self._threat_modeling
205
209
 
210
+ @property
211
+ def rules(self) -> AsyncRulesResource:
212
+ """Detection ruleset operations."""
213
+ return self._rules
214
+
215
+ @property
216
+ def appliances(self) -> AsyncAppliancesResource:
217
+ """Appliance configuration operations."""
218
+ return self._appliances
219
+
206
220
  async def close(self) -> None:
207
221
  """Close the underlying async HTTP client."""
208
222
  await self._http.close()
@@ -8,6 +8,7 @@ from blackshield.resources.ai_gateway import AiGatewayResource
8
8
  from blackshield.resources.ai_integrations import AiIntegrationsResource
9
9
  from blackshield.resources.alerts import AlertsResource
10
10
  from blackshield.resources.api_keys import ApiKeysResource
11
+ from blackshield.resources.appliances import AppliancesResource
11
12
  from blackshield.resources.assertions import AssertionsResource
12
13
  from blackshield.resources.audit import AuditResource
13
14
  from blackshield.resources.compliance import ComplianceResource
@@ -25,6 +26,7 @@ from blackshield.resources.oauth_apps import OAuthAppsResource
25
26
  from blackshield.resources.policies import PoliciesResource
26
27
  from blackshield.resources.remediation import RemediationResource
27
28
  from blackshield.resources.reports import ReportsResource
29
+ from blackshield.resources.rules import RulesResource
28
30
  from blackshield.resources.runbooks import RunbooksResource
29
31
  from blackshield.resources.security_agents import SecurityAgentsResource
30
32
  from blackshield.resources.security_reviews import SecurityReviewsResource
@@ -92,6 +94,8 @@ class BlackShield:
92
94
  self._oauth_apps = OAuthAppsResource(self._http)
93
95
  self._customer_notifications = CustomerNotificationsResource(self._http)
94
96
  self._threat_modeling = ThreatModelingResource(self._http)
97
+ self._rules = RulesResource(self._http)
98
+ self._appliances = AppliancesResource(self._http)
95
99
 
96
100
  @property
97
101
  def findings(self) -> FindingsResource:
@@ -238,6 +242,16 @@ class BlackShield:
238
242
  """Hosted threat-modeling sessions and exports."""
239
243
  return self._threat_modeling
240
244
 
245
+ @property
246
+ def rules(self) -> RulesResource:
247
+ """Detection ruleset operations."""
248
+ return self._rules
249
+
250
+ @property
251
+ def appliances(self) -> AppliancesResource:
252
+ """Appliance configuration operations."""
253
+ return self._appliances
254
+
241
255
  def close(self) -> None:
242
256
  """Close the underlying HTTP client."""
243
257
  self._http.close()
@@ -1,3 +1,3 @@
1
1
  """Single-source version for the BlackShield SDK."""
2
2
 
3
- __version__ = "0.2.2"
3
+ __version__ = "0.2.3"
@@ -2,6 +2,7 @@
2
2
 
3
3
  from blackshield.resources.ai_assets import AiAssetsResource, AsyncAiAssetsResource
4
4
  from blackshield.resources.ai_integrations import AiIntegrationsResource, AsyncAiIntegrationsResource
5
+ from blackshield.resources.appliances import AppliancesResource, AsyncAppliancesResource
5
6
  from blackshield.resources.assertions import AssertionsResource, AsyncAssertionsResource
6
7
  from blackshield.resources.customer_notifications import (
7
8
  AsyncCustomerNotificationsResource,
@@ -10,6 +11,7 @@ from blackshield.resources.customer_notifications import (
10
11
  from blackshield.resources.dast import AsyncDastResource, DastResource
11
12
  from blackshield.resources.evidence import AsyncEvidenceResource, EvidenceResource
12
13
  from blackshield.resources.oauth_apps import AsyncOAuthAppsResource, OAuthAppsResource
14
+ from blackshield.resources.rules import AsyncRulesResource, RulesResource
13
15
  from blackshield.resources.runbooks import AsyncRunbooksResource, RunbooksResource
14
16
  from blackshield.resources.security_agents import AsyncSecurityAgentsResource, SecurityAgentsResource
15
17
  from blackshield.resources.siem import AsyncSiemResource, SiemResource
@@ -18,14 +20,17 @@ from blackshield.resources.threat_modeling import AsyncThreatModelingResource, T
18
20
  __all__ = [
19
21
  "AiAssetsResource",
20
22
  "AiIntegrationsResource",
23
+ "AppliancesResource",
21
24
  "AssertionsResource",
22
25
  "AsyncAiAssetsResource",
23
26
  "AsyncAiIntegrationsResource",
27
+ "AsyncAppliancesResource",
24
28
  "AsyncAssertionsResource",
25
29
  "AsyncCustomerNotificationsResource",
26
30
  "AsyncDastResource",
27
31
  "AsyncEvidenceResource",
28
32
  "AsyncOAuthAppsResource",
33
+ "AsyncRulesResource",
29
34
  "AsyncRunbooksResource",
30
35
  "AsyncSecurityAgentsResource",
31
36
  "AsyncSiemResource",
@@ -34,6 +39,7 @@ __all__ = [
34
39
  "DastResource",
35
40
  "EvidenceResource",
36
41
  "OAuthAppsResource",
42
+ "RulesResource",
37
43
  "RunbooksResource",
38
44
  "SecurityAgentsResource",
39
45
  "SiemResource",
@@ -0,0 +1,54 @@
1
+ """Appliance configuration resource."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from blackshield.types.appliances import ApplianceConfig
8
+
9
+ if TYPE_CHECKING:
10
+ from blackshield._base_client import AsyncHTTPClient, SyncHTTPClient
11
+
12
+
13
+ class AppliancesResource:
14
+ """Synchronous appliance configuration operations."""
15
+
16
+ def __init__(self, client: SyncHTTPClient) -> None:
17
+ self._client = client
18
+
19
+ def get(self, kind: str) -> ApplianceConfig | None:
20
+ """Fetch latest published configuration for an appliance kind, or None if not found."""
21
+ try:
22
+ response = self._client.get(f"/api/v1/appliance-config/{kind}")
23
+ return ApplianceConfig.model_validate(response.json())
24
+ except Exception as exc:
25
+ if getattr(exc, "status_code", None) == 404:
26
+ return None
27
+ raise
28
+
29
+ def publish(self, kind: str, *, config: dict[str, Any]) -> ApplianceConfig:
30
+ """Publish a new appliance configuration."""
31
+ response = self._client.post(f"/api/v1/appliance-config/{kind}/publish", json={"config": config})
32
+ return ApplianceConfig.model_validate(response.json())
33
+
34
+
35
+ class AsyncAppliancesResource:
36
+ """Asynchronous appliance configuration operations."""
37
+
38
+ def __init__(self, client: AsyncHTTPClient) -> None:
39
+ self._client = client
40
+
41
+ async def get(self, kind: str) -> ApplianceConfig | None:
42
+ """Fetch latest published configuration for an appliance kind, or None if not found."""
43
+ try:
44
+ response = await self._client.get(f"/api/v1/appliance-config/{kind}")
45
+ return ApplianceConfig.model_validate(response.json())
46
+ except Exception as exc:
47
+ if getattr(exc, "status_code", None) == 404:
48
+ return None
49
+ raise
50
+
51
+ async def publish(self, kind: str, *, config: dict[str, Any]) -> ApplianceConfig:
52
+ """Publish a new appliance configuration."""
53
+ response = await self._client.post(f"/api/v1/appliance-config/{kind}/publish", json={"config": config})
54
+ return ApplianceConfig.model_validate(response.json())
@@ -11,6 +11,10 @@ from blackshield.types.identity import (
11
11
  OIDCConfig,
12
12
  SCIMConfig,
13
13
  SCIMRotateResponse,
14
+ SpiffeBundle,
15
+ SshCaInfo,
16
+ SshCertificate,
17
+ WorkloadAttestation,
14
18
  )
15
19
 
16
20
 
@@ -63,6 +67,26 @@ class IdentityResource:
63
67
  resp = self._client.get("/api/v1/identity/audit")
64
68
  return [IdentityAuditEvent.model_validate(e) for e in resp.json()]
65
69
 
70
+ def get_spiffe_bundle(self) -> SpiffeBundle:
71
+ """Fetch SPIFFE trust bundle."""
72
+ resp = self._client.get("/api/v1/identity/spiffe-bundle")
73
+ return SpiffeBundle.model_validate(resp.json())
74
+
75
+ def attest_workload(self, **payload: Any) -> WorkloadAttestation:
76
+ """Attest workload identity."""
77
+ resp = self._client.post("/api/v1/identity/workloads/attest", json=payload)
78
+ return WorkloadAttestation.model_validate(resp.json())
79
+
80
+ def get_ssh_ca_info(self) -> SshCaInfo:
81
+ """Fetch SSH CA public key information."""
82
+ resp = self._client.get("/api/v1/identity/ssh-ca")
83
+ return SshCaInfo.model_validate(resp.json())
84
+
85
+ def request_ssh_certificate(self, **payload: Any) -> SshCertificate:
86
+ """Request signed ephemeral SSH certificate."""
87
+ resp = self._client.post("/api/v1/identity/ssh-certificate", json=payload)
88
+ return SshCertificate.model_validate(resp.json())
89
+
66
90
 
67
91
  class AsyncIdentityResource:
68
92
  """Async identity operations."""
@@ -85,3 +109,23 @@ class AsyncIdentityResource:
85
109
  async def list_audit_events(self) -> list[IdentityAuditEvent]:
86
110
  resp = await self._client.get("/api/v1/identity/audit")
87
111
  return [IdentityAuditEvent.model_validate(e) for e in resp.json()]
112
+
113
+ async def get_spiffe_bundle(self) -> SpiffeBundle:
114
+ """Fetch SPIFFE trust bundle."""
115
+ resp = await self._client.get("/api/v1/identity/spiffe-bundle")
116
+ return SpiffeBundle.model_validate(resp.json())
117
+
118
+ async def attest_workload(self, **payload: Any) -> WorkloadAttestation:
119
+ """Attest workload identity."""
120
+ resp = await self._client.post("/api/v1/identity/workloads/attest", json=payload)
121
+ return WorkloadAttestation.model_validate(resp.json())
122
+
123
+ async def get_ssh_ca_info(self) -> SshCaInfo:
124
+ """Fetch SSH CA public key information."""
125
+ resp = await self._client.get("/api/v1/identity/ssh-ca")
126
+ return SshCaInfo.model_validate(resp.json())
127
+
128
+ async def request_ssh_certificate(self, **payload: Any) -> SshCertificate:
129
+ """Request signed ephemeral SSH certificate."""
130
+ resp = await self._client.post("/api/v1/identity/ssh-certificate", json=payload)
131
+ return SshCertificate.model_validate(resp.json())
@@ -0,0 +1,220 @@
1
+ """Detection ruleset operations resource for BlackShield SDK."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Sequence
6
+ from typing import Any, cast
7
+
8
+
9
+ class RulesResource:
10
+ """Synchronous detection ruleset operations."""
11
+
12
+ def __init__(self, client: Any) -> None:
13
+ self._client = client
14
+
15
+ def list(
16
+ self,
17
+ *,
18
+ target_type: str | None = None,
19
+ category: str | None = None,
20
+ severity: str | None = None,
21
+ is_system: bool | None = None,
22
+ search: str | None = None,
23
+ parent_rule_id: str | None = None,
24
+ page: int = 1,
25
+ page_size: int = 50,
26
+ ) -> dict[str, Any]:
27
+ """List detection rules visible to the tenant."""
28
+ params: dict[str, Any] = {"page": page, "page_size": page_size}
29
+ if target_type:
30
+ params["target_type"] = target_type
31
+ if category:
32
+ params["category"] = category
33
+ if severity:
34
+ params["severity"] = severity
35
+ if is_system is not None:
36
+ params["is_system"] = is_system
37
+ if search:
38
+ params["search"] = search
39
+ if parent_rule_id:
40
+ params["parent_rule_id"] = parent_rule_id
41
+
42
+ resp = self._client.get("/api/v1/rules", params=params)
43
+ return cast(dict[str, Any], resp.json())
44
+
45
+ def get(self, rule_id: str) -> dict[str, Any]:
46
+ """Retrieve a detection rule by ID."""
47
+ resp = self._client.get(f"/api/v1/rules/{rule_id}")
48
+ return cast(dict[str, Any], resp.json())
49
+
50
+ def stats(self) -> dict[str, Any]:
51
+ """Retrieve ruleset catalog summary metrics."""
52
+ resp = self._client.get("/api/v1/rules/stats")
53
+ return cast(dict[str, Any], resp.json())
54
+
55
+ def create(
56
+ self,
57
+ *,
58
+ rule_id: str,
59
+ title: str,
60
+ description: str = "",
61
+ severity: str = "medium",
62
+ category: str = "general",
63
+ target_type: str = "log",
64
+ match_logic: dict[str, Any] | None = None,
65
+ mitre_attack: Sequence[str] | None = None,
66
+ compliance_frameworks: Sequence[str] | None = None,
67
+ parent_rule_id: str | None = None,
68
+ is_enabled: bool = True,
69
+ remediation_guidance: str | None = None,
70
+ ) -> dict[str, Any]:
71
+ """Create a tenant custom detection rule."""
72
+ payload: dict[str, Any] = {
73
+ "rule_id": rule_id,
74
+ "title": title,
75
+ "description": description,
76
+ "severity": severity,
77
+ "category": category,
78
+ "target_type": target_type,
79
+ "match_logic": match_logic or {},
80
+ "mitre_attack": list(mitre_attack or []),
81
+ "compliance_frameworks": list(compliance_frameworks or []),
82
+ "parent_rule_id": parent_rule_id,
83
+ "is_enabled": is_enabled,
84
+ "remediation_guidance": remediation_guidance,
85
+ }
86
+ resp = self._client.post("/api/v1/rules", json=payload)
87
+ return cast(dict[str, Any], resp.json())
88
+
89
+ def update(self, rule_id: str, **kwargs: Any) -> dict[str, Any]:
90
+ """Update a tenant custom detection rule."""
91
+ resp = self._client.patch(f"/api/v1/rules/{rule_id}", json=kwargs)
92
+ return cast(dict[str, Any], resp.json())
93
+
94
+ def delete(self, rule_id: str) -> None:
95
+ """Delete a tenant custom detection rule."""
96
+ self._client.delete(f"/api/v1/rules/{rule_id}")
97
+
98
+ def test(
99
+ self,
100
+ *,
101
+ payload: dict[str, Any],
102
+ target_type: str = "log",
103
+ rule_id: str | None = None,
104
+ rule: dict[str, Any] | None = None,
105
+ ) -> dict[str, Any]:
106
+ """Dry-run test a rule against a sample payload."""
107
+ req: dict[str, Any] = {"payload": payload, "target_type": target_type}
108
+ if rule_id:
109
+ req["rule_id"] = rule_id
110
+ if rule:
111
+ req["rule"] = rule
112
+ resp = self._client.post("/api/v1/rules/test", json=req)
113
+ return cast(dict[str, Any], resp.json())
114
+
115
+
116
+ class AsyncRulesResource:
117
+ """Asynchronous detection ruleset operations."""
118
+
119
+ def __init__(self, client: Any) -> None:
120
+ self._client = client
121
+
122
+ async def list(
123
+ self,
124
+ *,
125
+ target_type: str | None = None,
126
+ category: str | None = None,
127
+ severity: str | None = None,
128
+ is_system: bool | None = None,
129
+ search: str | None = None,
130
+ parent_rule_id: str | None = None,
131
+ page: int = 1,
132
+ page_size: int = 50,
133
+ ) -> dict[str, Any]:
134
+ """List detection rules visible to the tenant asynchronously."""
135
+ params: dict[str, Any] = {"page": page, "page_size": page_size}
136
+ if target_type:
137
+ params["target_type"] = target_type
138
+ if category:
139
+ params["category"] = category
140
+ if severity:
141
+ params["severity"] = severity
142
+ if is_system is not None:
143
+ params["is_system"] = is_system
144
+ if search:
145
+ params["search"] = search
146
+ if parent_rule_id:
147
+ params["parent_rule_id"] = parent_rule_id
148
+
149
+ resp = await self._client.get("/api/v1/rules", params=params)
150
+ return cast(dict[str, Any], resp.json())
151
+
152
+ async def get(self, rule_id: str) -> dict[str, Any]:
153
+ """Retrieve a detection rule by ID asynchronously."""
154
+ resp = await self._client.get(f"/api/v1/rules/{rule_id}")
155
+ return cast(dict[str, Any], resp.json())
156
+
157
+ async def stats(self) -> dict[str, Any]:
158
+ """Retrieve ruleset catalog summary metrics asynchronously."""
159
+ resp = await self._client.get("/api/v1/rules/stats")
160
+ return cast(dict[str, Any], resp.json())
161
+
162
+ async def create(
163
+ self,
164
+ *,
165
+ rule_id: str,
166
+ title: str,
167
+ description: str = "",
168
+ severity: str = "medium",
169
+ category: str = "general",
170
+ target_type: str = "log",
171
+ match_logic: dict[str, Any] | None = None,
172
+ mitre_attack: Sequence[str] | None = None,
173
+ compliance_frameworks: Sequence[str] | None = None,
174
+ parent_rule_id: str | None = None,
175
+ is_enabled: bool = True,
176
+ remediation_guidance: str | None = None,
177
+ ) -> dict[str, Any]:
178
+ """Create a tenant custom detection rule asynchronously."""
179
+ payload: dict[str, Any] = {
180
+ "rule_id": rule_id,
181
+ "title": title,
182
+ "description": description,
183
+ "severity": severity,
184
+ "category": category,
185
+ "target_type": target_type,
186
+ "match_logic": match_logic or {},
187
+ "mitre_attack": list(mitre_attack or []),
188
+ "compliance_frameworks": list(compliance_frameworks or []),
189
+ "parent_rule_id": parent_rule_id,
190
+ "is_enabled": is_enabled,
191
+ "remediation_guidance": remediation_guidance,
192
+ }
193
+ resp = await self._client.post("/api/v1/rules", json=payload)
194
+ return cast(dict[str, Any], resp.json())
195
+
196
+ async def update(self, rule_id: str, **kwargs: Any) -> dict[str, Any]:
197
+ """Update a tenant custom detection rule asynchronously."""
198
+ resp = await self._client.patch(f"/api/v1/rules/{rule_id}", json=kwargs)
199
+ return cast(dict[str, Any], resp.json())
200
+
201
+ async def delete(self, rule_id: str) -> None:
202
+ """Delete a tenant custom detection rule asynchronously."""
203
+ await self._client.delete(f"/api/v1/rules/{rule_id}")
204
+
205
+ async def test(
206
+ self,
207
+ *,
208
+ payload: dict[str, Any],
209
+ target_type: str = "log",
210
+ rule_id: str | None = None,
211
+ rule: dict[str, Any] | None = None,
212
+ ) -> dict[str, Any]:
213
+ """Dry-run test a rule against a sample payload asynchronously."""
214
+ req: dict[str, Any] = {"payload": payload, "target_type": target_type}
215
+ if rule_id:
216
+ req["rule_id"] = rule_id
217
+ if rule:
218
+ req["rule"] = rule
219
+ resp = await self._client.post("/api/v1/rules/test", json=req)
220
+ return cast(dict[str, Any], resp.json())
@@ -6,9 +6,12 @@ import uuid
6
6
  from typing import Any
7
7
 
8
8
  from blackshield.types.security_agents import (
9
+ AgentAssignment,
9
10
  AgentCredential,
10
11
  AgentInstallation,
11
12
  AgentMission,
13
+ AgentRun,
14
+ AgentRunReport,
12
15
  AgentWorkflow,
13
16
  CapabilityOperation,
14
17
  OperatorMessage,
@@ -37,6 +40,10 @@ class SecurityAgentsResource:
37
40
  ).json()
38
41
  return [AgentInstallation.model_validate(item) for item in response["items"]]
39
42
 
43
+ def create_installation(self, **payload: Any) -> AgentInstallation:
44
+ response = self._client.post("/api/v1/security-agents/installations", json=payload).json()
45
+ return AgentInstallation.model_validate(response)
46
+
40
47
  def archive_installation(self, installation_id: uuid.UUID | str) -> AgentInstallation:
41
48
  response = self._client.post(f"/api/v1/security-agents/installations/{installation_id}/archive")
42
49
  return AgentInstallation.model_validate(response.json())
@@ -44,6 +51,31 @@ class SecurityAgentsResource:
44
51
  def delete_installation(self, installation_id: uuid.UUID | str) -> None:
45
52
  self._client.delete(f"/api/v1/security-agents/installations/{installation_id}")
46
53
 
54
+ def assignments(self) -> list[AgentAssignment]:
55
+ response = self._client.get("/api/v1/security-agents/assignments").json()
56
+ return [AgentAssignment.model_validate(item) for item in response["items"]]
57
+
58
+ def create_assignment(self, **payload: Any) -> AgentAssignment:
59
+ response = self._client.post("/api/v1/security-agents/assignments", json=payload).json()
60
+ return AgentAssignment.model_validate(response)
61
+
62
+ def runs(self, *, limit: int = 100, offset: int = 0, include_archived: bool = False) -> list[AgentRun]:
63
+ params = {"limit": limit, "offset": offset, "include_archived": include_archived}
64
+ response = self._client.get("/api/v1/security-agents/runs", params=params).json()
65
+ return [AgentRun.model_validate(item) for item in response["items"]]
66
+
67
+ def run(self, run_id: uuid.UUID | str) -> AgentRun:
68
+ response = self._client.get(f"/api/v1/security-agents/runs/{run_id}").json()
69
+ return AgentRun.model_validate(response)
70
+
71
+ def run_report(self, run_id: uuid.UUID | str) -> AgentRunReport:
72
+ response = self._client.get(f"/api/v1/security-agents/runs/{run_id}/report").json()
73
+ return AgentRunReport.model_validate(response)
74
+
75
+ def archive_run(self, run_id: uuid.UUID | str) -> AgentRun:
76
+ response = self._client.post(f"/api/v1/security-agents/runs/{run_id}/archive").json()
77
+ return AgentRun.model_validate(response)
78
+
47
79
  def credentials(self) -> list[AgentCredential]:
48
80
  response = self._client.get("/api/v1/security-agents/credentials").json()
49
81
  return [AgentCredential.model_validate(item) for item in response["items"]]
@@ -136,14 +168,14 @@ class AsyncSecurityAgentsResource:
136
168
  return [CapabilityOperation.model_validate(item) for item in response["operations"]]
137
169
 
138
170
  async def installations(self, *, include_archived: bool = False) -> list[AgentInstallation]:
139
- response = (
140
- await self._client.get(
141
- "/api/v1/security-agents/installations",
142
- params={"include_archived": include_archived},
143
- )
144
- ).json()
171
+ params = {"include_archived": include_archived}
172
+ response = (await self._client.get("/api/v1/security-agents/installations", params=params)).json()
145
173
  return [AgentInstallation.model_validate(item) for item in response["items"]]
146
174
 
175
+ async def create_installation(self, **payload: Any) -> AgentInstallation:
176
+ response = (await self._client.post("/api/v1/security-agents/installations", json=payload)).json()
177
+ return AgentInstallation.model_validate(response)
178
+
147
179
  async def archive_installation(self, installation_id: uuid.UUID | str) -> AgentInstallation:
148
180
  response = await self._client.post(f"/api/v1/security-agents/installations/{installation_id}/archive")
149
181
  return AgentInstallation.model_validate(response.json())
@@ -151,6 +183,30 @@ class AsyncSecurityAgentsResource:
151
183
  async def delete_installation(self, installation_id: uuid.UUID | str) -> None:
152
184
  await self._client.delete(f"/api/v1/security-agents/installations/{installation_id}")
153
185
 
186
+ async def assignments(self) -> list[AgentAssignment]:
187
+ response = (await self._client.get("/api/v1/security-agents/assignments")).json()
188
+ return [AgentAssignment.model_validate(item) for item in response["items"]]
189
+
190
+ async def create_assignment(self, **payload: Any) -> AgentAssignment:
191
+ response = (await self._client.post("/api/v1/security-agents/assignments", json=payload)).json()
192
+ return AgentAssignment.model_validate(response)
193
+
194
+ async def runs(self, *, limit: int = 100, offset: int = 0, include_archived: bool = False) -> list[AgentRun]:
195
+ params = {"limit": limit, "offset": offset, "include_archived": include_archived}
196
+ response = (await self._client.get("/api/v1/security-agents/runs", params=params)).json()
197
+ return [AgentRun.model_validate(item) for item in response["items"]]
198
+
199
+ async def run(self, run_id: uuid.UUID | str) -> AgentRun:
200
+ return AgentRun.model_validate((await self._client.get(f"/api/v1/security-agents/runs/{run_id}")).json())
201
+
202
+ async def run_report(self, run_id: uuid.UUID | str) -> AgentRunReport:
203
+ response = (await self._client.get(f"/api/v1/security-agents/runs/{run_id}/report")).json()
204
+ return AgentRunReport.model_validate(response)
205
+
206
+ async def archive_run(self, run_id: uuid.UUID | str) -> AgentRun:
207
+ res = await self._client.post(f"/api/v1/security-agents/runs/{run_id}/archive")
208
+ return AgentRun.model_validate(res.json())
209
+
154
210
  async def credentials(self) -> list[AgentCredential]:
155
211
  response = (await self._client.get("/api/v1/security-agents/credentials")).json()
156
212
  return [AgentCredential.model_validate(item) for item in response["items"]]