compliancelayer 0.1.0__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.
@@ -0,0 +1,140 @@
1
+ Metadata-Version: 2.4
2
+ Name: compliancelayer
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the ComplianceLayer security scanning API
5
+ Author-email: ComplianceLayer <dev@compliancelayer.net>
6
+ License: MIT
7
+ Project-URL: Homepage, https://compliancelayer.net
8
+ Project-URL: Documentation, https://compliancelayer.net/docs
9
+ Project-URL: Repository, https://github.com/Compliance-Layer/compliancelayer-python
10
+ Project-URL: Issues, https://github.com/Compliance-Layer/compliancelayer-python/issues
11
+ Keywords: security,compliance,scanning,msp,cyber-insurance
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: System Administrators
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Security
18
+ Classifier: Topic :: System :: Monitoring
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: httpx>=0.25.0
22
+
23
+ # ComplianceLayer Python SDK
24
+
25
+ Scan any domain for security compliance in seconds. Built for MSPs, DevOps engineers, and anyone who needs to assess domain security posture.
26
+
27
+ ## Install
28
+
29
+ ```bash
30
+ pip install compliancelayer
31
+ ```
32
+
33
+ ## Quick Start
34
+
35
+ ```python
36
+ from compliancelayer import ComplianceLayerClient
37
+
38
+ client = ComplianceLayerClient(api_key="cl_your_key_here")
39
+
40
+ # Full scan — runs all 16 security modules, waits for results
41
+ result = client.scan("example.com")
42
+ print(f"{result['domain']}: {result['grade']} ({result['score']}/100)")
43
+
44
+ for finding in result.get("findings", []):
45
+ print(f" [{finding['severity']}] {finding['finding']}")
46
+ ```
47
+
48
+ ## Free Scan (No API Key Required)
49
+
50
+ ```python
51
+ client = ComplianceLayerClient() # no key needed
52
+ result = client.free_scan("example.com")
53
+ print(f"Grade: {result['grade']} | Score: {result['score']}/100")
54
+ ```
55
+
56
+ Rate limited to 5 scans/hour. Returns grade + top 3 issues only.
57
+
58
+ ## Batch Scanning
59
+
60
+ Scan hundreds of domains at once:
61
+
62
+ ```python
63
+ domains = ["client1.com", "client2.com", "client3.com"]
64
+ results = client.batch_scan(domains)
65
+
66
+ for r in results:
67
+ print(f"{r['domain']}: {r['grade']}")
68
+ ```
69
+
70
+ ## Domain Monitoring
71
+
72
+ ```python
73
+ # Add a domain for recurring scans
74
+ client.add_domain("important-client.com", monitor=True)
75
+
76
+ # List monitored domains
77
+ for domain in client.domains():
78
+ print(f"{domain['domain']}: last scan {domain.get('last_scanned_at', 'never')}")
79
+ ```
80
+
81
+ ## Compliance Reports
82
+
83
+ Generate reports formatted for insurance broker submission:
84
+
85
+ ```python
86
+ report = client.compliance_report("example.com")
87
+ print(f"Compliance status: {report['status']}")
88
+ ```
89
+
90
+ ## What Gets Scanned
91
+
92
+ ComplianceLayer runs 16 security modules in under 60 seconds:
93
+
94
+ | Module | What it checks |
95
+ |--------|---------------|
96
+ | SSL/TLS | Certificate validity, expiration, chain, protocols |
97
+ | DNS | SPF, DMARC, DKIM, DNSSEC, CAA records |
98
+ | Headers | HSTS, CSP, X-Frame-Options, CORS, permissions |
99
+ | Ports | ~100 common ports for exposure |
100
+ | Email Auth | SPF alignment, DMARC enforcement, DKIM signing |
101
+ | Blacklists | 35+ blocklist databases |
102
+ | Breaches | Known data breach exposure |
103
+ | Cookies | Secure/HttpOnly/SameSite flags |
104
+ | Subdomains | Subdomain enumeration and exposure |
105
+ | Tech Stack | Framework and server fingerprinting |
106
+ | WAF | Web Application Firewall detection |
107
+ | Reputation | Domain reputation scoring |
108
+ | JavaScript | External script audit |
109
+ | Trackers | Third-party tracker detection |
110
+ | WHOIS | Registration and ownership data |
111
+ | Compliance | Insurance-relevant compliance mapping |
112
+
113
+ ## Pricing
114
+
115
+ | Plan | Price | Scans/month | Domains |
116
+ |------|-------|-------------|---------|
117
+ | Free | $0 | 10 | 1 |
118
+ | Pro | $99/mo | 1,000 | 50 |
119
+ | Enterprise | $499/mo | 5,000 | 200 |
120
+
121
+ Get your API key at [compliancelayer.net](https://compliancelayer.net)
122
+
123
+ ## Error Handling
124
+
125
+ ```python
126
+ from compliancelayer import ComplianceLayerClient, RateLimitError, AuthenticationError
127
+
128
+ client = ComplianceLayerClient(api_key="cl_your_key")
129
+
130
+ try:
131
+ result = client.scan("example.com")
132
+ except AuthenticationError:
133
+ print("Invalid API key")
134
+ except RateLimitError as e:
135
+ print(f"Rate limited. Retry after {e.retry_after}s")
136
+ ```
137
+
138
+ ## License
139
+
140
+ MIT
@@ -0,0 +1,118 @@
1
+ # ComplianceLayer Python SDK
2
+
3
+ Scan any domain for security compliance in seconds. Built for MSPs, DevOps engineers, and anyone who needs to assess domain security posture.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pip install compliancelayer
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from compliancelayer import ComplianceLayerClient
15
+
16
+ client = ComplianceLayerClient(api_key="cl_your_key_here")
17
+
18
+ # Full scan — runs all 16 security modules, waits for results
19
+ result = client.scan("example.com")
20
+ print(f"{result['domain']}: {result['grade']} ({result['score']}/100)")
21
+
22
+ for finding in result.get("findings", []):
23
+ print(f" [{finding['severity']}] {finding['finding']}")
24
+ ```
25
+
26
+ ## Free Scan (No API Key Required)
27
+
28
+ ```python
29
+ client = ComplianceLayerClient() # no key needed
30
+ result = client.free_scan("example.com")
31
+ print(f"Grade: {result['grade']} | Score: {result['score']}/100")
32
+ ```
33
+
34
+ Rate limited to 5 scans/hour. Returns grade + top 3 issues only.
35
+
36
+ ## Batch Scanning
37
+
38
+ Scan hundreds of domains at once:
39
+
40
+ ```python
41
+ domains = ["client1.com", "client2.com", "client3.com"]
42
+ results = client.batch_scan(domains)
43
+
44
+ for r in results:
45
+ print(f"{r['domain']}: {r['grade']}")
46
+ ```
47
+
48
+ ## Domain Monitoring
49
+
50
+ ```python
51
+ # Add a domain for recurring scans
52
+ client.add_domain("important-client.com", monitor=True)
53
+
54
+ # List monitored domains
55
+ for domain in client.domains():
56
+ print(f"{domain['domain']}: last scan {domain.get('last_scanned_at', 'never')}")
57
+ ```
58
+
59
+ ## Compliance Reports
60
+
61
+ Generate reports formatted for insurance broker submission:
62
+
63
+ ```python
64
+ report = client.compliance_report("example.com")
65
+ print(f"Compliance status: {report['status']}")
66
+ ```
67
+
68
+ ## What Gets Scanned
69
+
70
+ ComplianceLayer runs 16 security modules in under 60 seconds:
71
+
72
+ | Module | What it checks |
73
+ |--------|---------------|
74
+ | SSL/TLS | Certificate validity, expiration, chain, protocols |
75
+ | DNS | SPF, DMARC, DKIM, DNSSEC, CAA records |
76
+ | Headers | HSTS, CSP, X-Frame-Options, CORS, permissions |
77
+ | Ports | ~100 common ports for exposure |
78
+ | Email Auth | SPF alignment, DMARC enforcement, DKIM signing |
79
+ | Blacklists | 35+ blocklist databases |
80
+ | Breaches | Known data breach exposure |
81
+ | Cookies | Secure/HttpOnly/SameSite flags |
82
+ | Subdomains | Subdomain enumeration and exposure |
83
+ | Tech Stack | Framework and server fingerprinting |
84
+ | WAF | Web Application Firewall detection |
85
+ | Reputation | Domain reputation scoring |
86
+ | JavaScript | External script audit |
87
+ | Trackers | Third-party tracker detection |
88
+ | WHOIS | Registration and ownership data |
89
+ | Compliance | Insurance-relevant compliance mapping |
90
+
91
+ ## Pricing
92
+
93
+ | Plan | Price | Scans/month | Domains |
94
+ |------|-------|-------------|---------|
95
+ | Free | $0 | 10 | 1 |
96
+ | Pro | $99/mo | 1,000 | 50 |
97
+ | Enterprise | $499/mo | 5,000 | 200 |
98
+
99
+ Get your API key at [compliancelayer.net](https://compliancelayer.net)
100
+
101
+ ## Error Handling
102
+
103
+ ```python
104
+ from compliancelayer import ComplianceLayerClient, RateLimitError, AuthenticationError
105
+
106
+ client = ComplianceLayerClient(api_key="cl_your_key")
107
+
108
+ try:
109
+ result = client.scan("example.com")
110
+ except AuthenticationError:
111
+ print("Invalid API key")
112
+ except RateLimitError as e:
113
+ print(f"Rate limited. Retry after {e.retry_after}s")
114
+ ```
115
+
116
+ ## License
117
+
118
+ MIT
@@ -0,0 +1,20 @@
1
+ """ComplianceLayer Python SDK — scan any domain for security compliance in seconds."""
2
+
3
+ from .client import ComplianceLayerClient
4
+ from .exceptions import (
5
+ ComplianceLayerError,
6
+ AuthenticationError,
7
+ RateLimitError,
8
+ ScanError,
9
+ TimeoutError,
10
+ )
11
+
12
+ __version__ = "0.1.0"
13
+ __all__ = [
14
+ "ComplianceLayerClient",
15
+ "ComplianceLayerError",
16
+ "AuthenticationError",
17
+ "RateLimitError",
18
+ "ScanError",
19
+ "TimeoutError",
20
+ ]
@@ -0,0 +1,288 @@
1
+ """ComplianceLayer API client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import time
6
+ from typing import Any, Dict, List, Optional
7
+
8
+ import httpx
9
+
10
+ from .exceptions import (
11
+ AuthenticationError,
12
+ ComplianceLayerError,
13
+ RateLimitError,
14
+ ScanError,
15
+ TimeoutError,
16
+ )
17
+
18
+ DEFAULT_BASE_URL = "https://api.compliancelayer.net"
19
+ DEFAULT_TIMEOUT = 30.0
20
+ DEFAULT_POLL_INTERVAL = 3.0
21
+ DEFAULT_POLL_TIMEOUT = 120.0
22
+
23
+
24
+ class ComplianceLayerClient:
25
+ """Client for the ComplianceLayer security scanning API.
26
+
27
+ Usage::
28
+
29
+ from compliancelayer import ComplianceLayerClient
30
+
31
+ client = ComplianceLayerClient(api_key="cl_your_key_here")
32
+
33
+ # Quick free scan (limited results, no API key required)
34
+ result = client.free_scan("example.com")
35
+ print(f"{result['domain']}: {result['grade']} ({result['score']}/100)")
36
+
37
+ # Full scan (requires API key + paid plan)
38
+ result = client.scan("example.com")
39
+ for category, data in result["categories"].items():
40
+ print(f" {category}: {data['grade']} ({data['score']}/100)")
41
+
42
+ # Batch scan
43
+ results = client.batch_scan(["example.com", "github.com", "stripe.com"])
44
+ """
45
+
46
+ def __init__(
47
+ self,
48
+ api_key: str | None = None,
49
+ base_url: str = DEFAULT_BASE_URL,
50
+ timeout: float = DEFAULT_TIMEOUT,
51
+ ):
52
+ self.api_key = api_key
53
+ self.base_url = base_url.rstrip("/")
54
+ headers = {"User-Agent": f"compliancelayer-python/0.1.0"}
55
+ if api_key:
56
+ headers["Authorization"] = f"Bearer {api_key}"
57
+ self._client = httpx.Client(
58
+ base_url=self.base_url,
59
+ headers=headers,
60
+ timeout=timeout,
61
+ )
62
+
63
+ def close(self) -> None:
64
+ """Close the underlying HTTP client."""
65
+ self._client.close()
66
+
67
+ def __enter__(self) -> ComplianceLayerClient:
68
+ return self
69
+
70
+ def __exit__(self, *args) -> None:
71
+ self.close()
72
+
73
+ # ------------------------------------------------------------------ #
74
+ # Internal helpers
75
+ # ------------------------------------------------------------------ #
76
+
77
+ def _request(self, method: str, path: str, **kwargs) -> dict[str, Any]:
78
+ """Make an API request and handle errors."""
79
+ resp = self._client.request(method, f"/api/v1{path}", **kwargs)
80
+ if resp.status_code == 401:
81
+ raise AuthenticationError(
82
+ "Invalid or missing API key. Get one at https://compliancelayer.net",
83
+ status_code=401,
84
+ )
85
+ if resp.status_code == 429:
86
+ retry_after = resp.headers.get("Retry-After")
87
+ raise RateLimitError(
88
+ "Rate limit exceeded. Upgrade your plan for higher limits.",
89
+ status_code=429,
90
+ retry_after=int(retry_after) if retry_after else None,
91
+ )
92
+ if resp.status_code >= 400:
93
+ try:
94
+ body = resp.json()
95
+ except Exception:
96
+ body = {"detail": resp.text}
97
+ msg = body.get("detail") or body.get("message") or resp.text
98
+ raise ComplianceLayerError(str(msg), status_code=resp.status_code, response=body)
99
+ return resp.json()
100
+
101
+ # ------------------------------------------------------------------ #
102
+ # Public API
103
+ # ------------------------------------------------------------------ #
104
+
105
+ def free_scan(self, domain: str) -> dict[str, Any]:
106
+ """Run a free scan. Returns grade + top 3 issues. No API key required.
107
+
108
+ Rate limited to 5 scans/hour per IP.
109
+
110
+ Args:
111
+ domain: The domain to scan (e.g., "example.com").
112
+
113
+ Returns:
114
+ dict with keys: domain, grade, score, top_findings, modules, etc.
115
+ """
116
+ return self._request("POST", "/scan/free", json={"domain": domain})
117
+
118
+ def scan(
119
+ self,
120
+ domain: str,
121
+ *,
122
+ wait: bool = True,
123
+ poll_interval: float = DEFAULT_POLL_INTERVAL,
124
+ poll_timeout: float = DEFAULT_POLL_TIMEOUT,
125
+ ) -> dict[str, Any]:
126
+ """Run a full authenticated scan across all 16 modules.
127
+
128
+ By default, submits the scan job and polls until complete.
129
+ Set ``wait=False`` to get the job_id immediately and poll manually.
130
+
131
+ Args:
132
+ domain: The domain to scan.
133
+ wait: If True, poll until the scan finishes (default).
134
+ poll_interval: Seconds between poll attempts (default 3).
135
+ poll_timeout: Max seconds to wait before raising TimeoutError (default 120).
136
+
137
+ Returns:
138
+ dict with keys: job_id, status, domain, grade, score, categories, findings.
139
+ """
140
+ if not self.api_key:
141
+ raise AuthenticationError("API key required for full scans. Use free_scan() for unauthenticated access.")
142
+
143
+ resp = self._request("POST", "/scan", json={"domain": domain})
144
+
145
+ if not wait:
146
+ return resp
147
+
148
+ job_id = resp.get("job_id")
149
+ if not job_id:
150
+ return resp
151
+
152
+ return self.poll_scan(job_id, interval=poll_interval, timeout=poll_timeout)
153
+
154
+ def poll_scan(
155
+ self,
156
+ job_id: str,
157
+ *,
158
+ interval: float = DEFAULT_POLL_INTERVAL,
159
+ timeout: float = DEFAULT_POLL_TIMEOUT,
160
+ ) -> dict[str, Any]:
161
+ """Poll a scan job until it completes or times out.
162
+
163
+ Args:
164
+ job_id: The job ID from scan().
165
+ interval: Seconds between poll attempts.
166
+ timeout: Max seconds to wait.
167
+
168
+ Returns:
169
+ The completed scan result.
170
+
171
+ Raises:
172
+ TimeoutError: If the scan doesn't complete within the timeout.
173
+ ScanError: If the scan fails.
174
+ """
175
+ deadline = time.monotonic() + timeout
176
+ while time.monotonic() < deadline:
177
+ result = self._request("GET", f"/scan/{job_id}")
178
+ status = result.get("status", "")
179
+ if status == "complete":
180
+ return result
181
+ if status == "failed":
182
+ raise ScanError(
183
+ f"Scan failed for job {job_id}",
184
+ response=result,
185
+ )
186
+ time.sleep(interval)
187
+
188
+ raise TimeoutError(
189
+ f"Scan {job_id} did not complete within {timeout}s. "
190
+ f"Last status: {result.get('status')}. Try increasing poll_timeout.",
191
+ )
192
+
193
+ def batch_scan(
194
+ self,
195
+ domains: list[str],
196
+ *,
197
+ wait: bool = True,
198
+ poll_interval: float = DEFAULT_POLL_INTERVAL,
199
+ poll_timeout: float = 300.0,
200
+ ) -> list[dict[str, Any]]:
201
+ """Scan multiple domains. Submits all jobs, then polls until all complete.
202
+
203
+ Args:
204
+ domains: List of domains to scan.
205
+ wait: If True, wait for all scans to complete.
206
+ poll_interval: Seconds between poll cycles.
207
+ poll_timeout: Max seconds to wait for all scans.
208
+
209
+ Returns:
210
+ List of scan results (same shape as scan()).
211
+ """
212
+ if not self.api_key:
213
+ raise AuthenticationError("API key required for batch scans.")
214
+
215
+ resp = self._request("POST", "/batch", json={"domains": domains})
216
+ jobs = resp.get("jobs", [])
217
+
218
+ if not wait or not jobs:
219
+ return jobs
220
+
221
+ deadline = time.monotonic() + poll_timeout
222
+ results = {}
223
+ pending = {j["job_id"] for j in jobs if "job_id" in j}
224
+
225
+ while pending and time.monotonic() < deadline:
226
+ for job_id in list(pending):
227
+ try:
228
+ result = self._request("GET", f"/scan/{job_id}")
229
+ except ComplianceLayerError:
230
+ continue
231
+ status = result.get("status", "")
232
+ if status in ("complete", "failed"):
233
+ results[job_id] = result
234
+ pending.discard(job_id)
235
+ if pending:
236
+ time.sleep(poll_interval)
237
+
238
+ for job_id in pending:
239
+ results[job_id] = {"job_id": job_id, "status": "timeout"}
240
+
241
+ return [results.get(j["job_id"], j) for j in jobs]
242
+
243
+ def domains(self) -> list[dict[str, Any]]:
244
+ """List all monitored domains.
245
+
246
+ Returns:
247
+ List of domain objects with monitoring config.
248
+ """
249
+ return self._request("GET", "/domains")
250
+
251
+ def add_domain(self, domain: str, *, monitor: bool = True) -> dict[str, Any]:
252
+ """Add a domain for monitoring.
253
+
254
+ Args:
255
+ domain: The domain to add.
256
+ monitor: Enable recurring scans (default True).
257
+
258
+ Returns:
259
+ The created domain object.
260
+ """
261
+ return self._request("POST", "/domains", json={"domain": domain, "monitor": monitor})
262
+
263
+ def compliance_report(self, domain: str) -> dict[str, Any]:
264
+ """Get a compliance report for a domain.
265
+
266
+ Args:
267
+ domain: The domain to get the report for.
268
+
269
+ Returns:
270
+ Compliance report with insurance-relevant findings.
271
+ """
272
+ return self._request("GET", f"/compliance/{domain}")
273
+
274
+ def usage(self) -> dict[str, Any]:
275
+ """Get current plan usage stats.
276
+
277
+ Returns:
278
+ dict with scans_used, scans_limit, domains_used, domains_limit, plan.
279
+ """
280
+ return self._request("GET", "/usage")
281
+
282
+ def status(self) -> dict[str, Any]:
283
+ """Check API health.
284
+
285
+ Returns:
286
+ dict with status, version, uptime.
287
+ """
288
+ return self._request("GET", "/status")
@@ -0,0 +1,32 @@
1
+ """ComplianceLayer SDK exceptions."""
2
+
3
+ from typing import Optional
4
+
5
+
6
+ class ComplianceLayerError(Exception):
7
+ """Base exception for all SDK errors."""
8
+
9
+ def __init__(self, message: str, status_code: Optional[int] = None, response: Optional[dict] = None):
10
+ super().__init__(message)
11
+ self.status_code = status_code
12
+ self.response = response
13
+
14
+
15
+ class AuthenticationError(ComplianceLayerError):
16
+ """Raised when the API key is invalid or missing."""
17
+
18
+
19
+ class RateLimitError(ComplianceLayerError):
20
+ """Raised when the rate limit is exceeded."""
21
+
22
+ def __init__(self, message: str, retry_after: Optional[int] = None, **kwargs):
23
+ super().__init__(message, **kwargs)
24
+ self.retry_after = retry_after
25
+
26
+
27
+ class ScanError(ComplianceLayerError):
28
+ """Raised when a scan fails."""
29
+
30
+
31
+ class TimeoutError(ComplianceLayerError):
32
+ """Raised when a scan times out waiting for results."""
@@ -0,0 +1,140 @@
1
+ Metadata-Version: 2.4
2
+ Name: compliancelayer
3
+ Version: 0.1.0
4
+ Summary: Python SDK for the ComplianceLayer security scanning API
5
+ Author-email: ComplianceLayer <dev@compliancelayer.net>
6
+ License: MIT
7
+ Project-URL: Homepage, https://compliancelayer.net
8
+ Project-URL: Documentation, https://compliancelayer.net/docs
9
+ Project-URL: Repository, https://github.com/Compliance-Layer/compliancelayer-python
10
+ Project-URL: Issues, https://github.com/Compliance-Layer/compliancelayer-python/issues
11
+ Keywords: security,compliance,scanning,msp,cyber-insurance
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: System Administrators
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Security
18
+ Classifier: Topic :: System :: Monitoring
19
+ Requires-Python: >=3.9
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: httpx>=0.25.0
22
+
23
+ # ComplianceLayer Python SDK
24
+
25
+ Scan any domain for security compliance in seconds. Built for MSPs, DevOps engineers, and anyone who needs to assess domain security posture.
26
+
27
+ ## Install
28
+
29
+ ```bash
30
+ pip install compliancelayer
31
+ ```
32
+
33
+ ## Quick Start
34
+
35
+ ```python
36
+ from compliancelayer import ComplianceLayerClient
37
+
38
+ client = ComplianceLayerClient(api_key="cl_your_key_here")
39
+
40
+ # Full scan — runs all 16 security modules, waits for results
41
+ result = client.scan("example.com")
42
+ print(f"{result['domain']}: {result['grade']} ({result['score']}/100)")
43
+
44
+ for finding in result.get("findings", []):
45
+ print(f" [{finding['severity']}] {finding['finding']}")
46
+ ```
47
+
48
+ ## Free Scan (No API Key Required)
49
+
50
+ ```python
51
+ client = ComplianceLayerClient() # no key needed
52
+ result = client.free_scan("example.com")
53
+ print(f"Grade: {result['grade']} | Score: {result['score']}/100")
54
+ ```
55
+
56
+ Rate limited to 5 scans/hour. Returns grade + top 3 issues only.
57
+
58
+ ## Batch Scanning
59
+
60
+ Scan hundreds of domains at once:
61
+
62
+ ```python
63
+ domains = ["client1.com", "client2.com", "client3.com"]
64
+ results = client.batch_scan(domains)
65
+
66
+ for r in results:
67
+ print(f"{r['domain']}: {r['grade']}")
68
+ ```
69
+
70
+ ## Domain Monitoring
71
+
72
+ ```python
73
+ # Add a domain for recurring scans
74
+ client.add_domain("important-client.com", monitor=True)
75
+
76
+ # List monitored domains
77
+ for domain in client.domains():
78
+ print(f"{domain['domain']}: last scan {domain.get('last_scanned_at', 'never')}")
79
+ ```
80
+
81
+ ## Compliance Reports
82
+
83
+ Generate reports formatted for insurance broker submission:
84
+
85
+ ```python
86
+ report = client.compliance_report("example.com")
87
+ print(f"Compliance status: {report['status']}")
88
+ ```
89
+
90
+ ## What Gets Scanned
91
+
92
+ ComplianceLayer runs 16 security modules in under 60 seconds:
93
+
94
+ | Module | What it checks |
95
+ |--------|---------------|
96
+ | SSL/TLS | Certificate validity, expiration, chain, protocols |
97
+ | DNS | SPF, DMARC, DKIM, DNSSEC, CAA records |
98
+ | Headers | HSTS, CSP, X-Frame-Options, CORS, permissions |
99
+ | Ports | ~100 common ports for exposure |
100
+ | Email Auth | SPF alignment, DMARC enforcement, DKIM signing |
101
+ | Blacklists | 35+ blocklist databases |
102
+ | Breaches | Known data breach exposure |
103
+ | Cookies | Secure/HttpOnly/SameSite flags |
104
+ | Subdomains | Subdomain enumeration and exposure |
105
+ | Tech Stack | Framework and server fingerprinting |
106
+ | WAF | Web Application Firewall detection |
107
+ | Reputation | Domain reputation scoring |
108
+ | JavaScript | External script audit |
109
+ | Trackers | Third-party tracker detection |
110
+ | WHOIS | Registration and ownership data |
111
+ | Compliance | Insurance-relevant compliance mapping |
112
+
113
+ ## Pricing
114
+
115
+ | Plan | Price | Scans/month | Domains |
116
+ |------|-------|-------------|---------|
117
+ | Free | $0 | 10 | 1 |
118
+ | Pro | $99/mo | 1,000 | 50 |
119
+ | Enterprise | $499/mo | 5,000 | 200 |
120
+
121
+ Get your API key at [compliancelayer.net](https://compliancelayer.net)
122
+
123
+ ## Error Handling
124
+
125
+ ```python
126
+ from compliancelayer import ComplianceLayerClient, RateLimitError, AuthenticationError
127
+
128
+ client = ComplianceLayerClient(api_key="cl_your_key")
129
+
130
+ try:
131
+ result = client.scan("example.com")
132
+ except AuthenticationError:
133
+ print("Invalid API key")
134
+ except RateLimitError as e:
135
+ print(f"Rate limited. Retry after {e.retry_after}s")
136
+ ```
137
+
138
+ ## License
139
+
140
+ MIT
@@ -0,0 +1,10 @@
1
+ README.md
2
+ pyproject.toml
3
+ compliancelayer/__init__.py
4
+ compliancelayer/client.py
5
+ compliancelayer/exceptions.py
6
+ compliancelayer.egg-info/PKG-INFO
7
+ compliancelayer.egg-info/SOURCES.txt
8
+ compliancelayer.egg-info/dependency_links.txt
9
+ compliancelayer.egg-info/requires.txt
10
+ compliancelayer.egg-info/top_level.txt
@@ -0,0 +1 @@
1
+ httpx>=0.25.0
@@ -0,0 +1 @@
1
+ compliancelayer
@@ -0,0 +1,34 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "compliancelayer"
7
+ version = "0.1.0"
8
+ description = "Python SDK for the ComplianceLayer security scanning API"
9
+ readme = "README.md"
10
+ license = {text = "MIT"}
11
+ requires-python = ">=3.9"
12
+ authors = [{name = "ComplianceLayer", email = "dev@compliancelayer.net"}]
13
+ keywords = ["security", "compliance", "scanning", "msp", "cyber-insurance"]
14
+ classifiers = [
15
+ "Development Status :: 4 - Beta",
16
+ "Intended Audience :: Developers",
17
+ "Intended Audience :: System Administrators",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Topic :: Security",
21
+ "Topic :: System :: Monitoring",
22
+ ]
23
+ dependencies = [
24
+ "httpx>=0.25.0",
25
+ ]
26
+
27
+ [project.urls]
28
+ Homepage = "https://compliancelayer.net"
29
+ Documentation = "https://compliancelayer.net/docs"
30
+ Repository = "https://github.com/Compliance-Layer/compliancelayer-python"
31
+ Issues = "https://github.com/Compliance-Layer/compliancelayer-python/issues"
32
+
33
+ [tool.setuptools.packages.find]
34
+ include = ["compliancelayer*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+