contrastapi 1.22.3__py3-none-any.whl

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.
contrastapi/models.py ADDED
@@ -0,0 +1,661 @@
1
+ """Lightweight TypedDict response models — IDE autocomplete, zero runtime cost.
2
+
3
+ Every TypedDict mirrors the corresponding `app/schemas.py` Pydantic class on the
4
+ server. Field names are wire-exact (kept in sync via the v1.22.3 round-2 audit
5
+ against `app/schemas.py`). Drift here = silent KeyError downstream, so the rule
6
+ is: **if the server schema doesn't have it, the TypedDict doesn't either**.
7
+
8
+ Why TypedDict (not Pydantic)?
9
+ * No additional dependency beyond `httpx`.
10
+ * Zero validation overhead — server already validates on response.
11
+ * `total=False` everywhere — every key is optional from the SDK's perspective
12
+ so omitted fields (Pydantic `response_model_exclude_none=True`) don't
13
+ trigger spurious type errors.
14
+
15
+ Coverage strategy: top-level keys only. Nested complex fields (`kev`, `cvss`,
16
+ `reputation`, `verdict.falsifiable_fields`, `pivot_hint.params`, etc.) stay as
17
+ `dict[str, Any]` so server-side schema drift on internal subtrees doesn't
18
+ break IDE completion for SDK consumers.
19
+
20
+ For richer typing (full nested validation), use the server's `app.schemas`
21
+ Pydantic models directly — they remain the wire-contract source of truth.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from typing import Any, TypedDict
27
+
28
+ # --- Cross-cutting shapes ---
29
+
30
+
31
+ class ErrorBody(TypedDict, total=False):
32
+ code: str
33
+ message: str
34
+ retry_after_seconds: int | None
35
+ upgrade_url: str | None
36
+ docs_url: str | None
37
+
38
+
39
+ class ErrorEnvelope(TypedDict, total=False):
40
+ """Wire-level error response (v1.22.2+)."""
41
+
42
+ error: ErrorBody
43
+ # Top-level back-compat extensions: hint, tier, limit, upgrade, field,
44
+ # received, suggestion, support, reset_in, error_code, docs.
45
+
46
+
47
+ class Verdict(TypedDict, total=False):
48
+ deterministic: bool
49
+ falsifiable_fields: list[str]
50
+ sources_queried: list[str]
51
+ sources_unavailable: list[str]
52
+ completeness: str
53
+
54
+
55
+ class PivotHint(TypedDict, total=False):
56
+ tool: str
57
+ value: str
58
+ reason: str
59
+ params: dict[str, Any]
60
+
61
+
62
+ # --- CVE / KEV / CWE / Exploit ---
63
+
64
+
65
+ class CveResponse(TypedDict, total=False):
66
+ cve_id: str
67
+ summary: str
68
+ description: str
69
+ severity: str
70
+ cvss_v3: float
71
+ cvss_breakdown: dict[str, Any]
72
+ cwe_id: str
73
+ epss: dict[str, Any]
74
+ kev: dict[str, Any]
75
+ affected_products: list[dict[str, Any]]
76
+ total_products: int
77
+ published: str
78
+ modified: str
79
+ references: list[str]
80
+ total_references: int
81
+ patch_available: bool
82
+ patch_url: str
83
+ sources: list[str]
84
+ verdict: Verdict
85
+ next_calls: list[PivotHint]
86
+
87
+
88
+ class CveSearchResponse(TypedDict, total=False):
89
+ count: int
90
+ total: int
91
+ truncated: bool
92
+ offset: int
93
+ summary: str
94
+ results: list[dict[str, Any]]
95
+ query_echo: dict[str, Any]
96
+ next_offset: int | None
97
+ hint: dict[str, Any]
98
+ verdict: Verdict
99
+ next_calls: list[PivotHint]
100
+
101
+
102
+ class KevDetailResponse(TypedDict, total=False):
103
+ cve_id: str
104
+ in_kev: bool
105
+ date_added: str
106
+ due_date: str
107
+ required_action: str
108
+ known_ransomware_use: bool
109
+ vendor_project: str
110
+ product: str
111
+ vulnerability_name: str
112
+ short_description: str
113
+ notes: str
114
+ cwes: list[str]
115
+ verdict: Verdict
116
+ next_calls: list[PivotHint]
117
+
118
+
119
+ class CweLookupResponse(TypedDict, total=False):
120
+ cwe_id: str
121
+ name: str
122
+ description: str
123
+ extended_description: str
124
+ abstract_type: str
125
+ status: str
126
+ likelihood: str
127
+ mitigations: list[str]
128
+ examples: list[str]
129
+ parent_cwe: str
130
+ child_cwes: list[str]
131
+ cve_count: int
132
+ total_mitigations: int | None
133
+ total_examples: int | None
134
+ updated_at: str
135
+ verdict: Verdict
136
+ next_calls: list[PivotHint]
137
+
138
+
139
+ class ExploitResponse(TypedDict, total=False):
140
+ cve_id: str
141
+ exploits_found: int
142
+ sources: dict[str, Any]
143
+ has_public_exploit: bool
144
+ exploits: list[dict[str, Any]]
145
+ summary: str
146
+ verdict: Verdict
147
+ next_calls: list[PivotHint]
148
+
149
+
150
+ # --- Bulk parity (v1.21.0+ unified status enum: ok/error/not_found/invalid_format) ---
151
+
152
+
153
+ class BulkCveItem(TypedDict, total=False):
154
+ cve_id: str
155
+ status: str
156
+ cve: CveResponse | None
157
+ error: str
158
+
159
+
160
+ class BulkCveResponse(TypedDict, total=False):
161
+ results: list[BulkCveItem]
162
+ total: int
163
+ successful: int
164
+ failed: int
165
+ timed_out: int
166
+ not_found: int
167
+ partial: bool
168
+ summary: str
169
+ verdict: Verdict
170
+ next_calls: list[PivotHint]
171
+
172
+
173
+ class BulkIocItem(TypedDict, total=False):
174
+ indicator: str
175
+ status: str
176
+ ioc: dict[str, Any]
177
+ error: str
178
+
179
+
180
+ class BulkIocResponse(TypedDict, total=False):
181
+ results: list[BulkIocItem]
182
+ total: int
183
+ successful: int
184
+ failed: int
185
+ timed_out: int
186
+ not_found: int
187
+ invalid: int
188
+ partial: bool
189
+ summary: str
190
+ verdict: Verdict
191
+ next_calls: list[PivotHint]
192
+
193
+
194
+ class BulkAtlasTechniqueItem(TypedDict, total=False):
195
+ technique_id: str
196
+ status: str
197
+ technique: dict[str, Any]
198
+ error: str
199
+
200
+
201
+ class BulkAtlasTechniqueResponse(TypedDict, total=False):
202
+ results: list[BulkAtlasTechniqueItem]
203
+ total: int
204
+ successful: int
205
+ failed: int
206
+ partial: bool
207
+ summary: str
208
+ verdict: Verdict
209
+ next_calls: list[PivotHint]
210
+
211
+
212
+ # --- IOC / Hash / Phishing / Password ---
213
+
214
+
215
+ class IocResponse(TypedDict, total=False):
216
+ indicator: str
217
+ type: str # ip | domain | url | hash | unknown
218
+ threat_level: str # none | low | medium | high
219
+ sources: dict[str, Any]
220
+ summary: str
221
+ verdict: Verdict
222
+ next_calls: list[PivotHint]
223
+
224
+
225
+ class HashResponse(TypedDict, total=False):
226
+ hash: str
227
+ hash_type: str
228
+ found: bool
229
+ malware_family: str | None
230
+ file_type: str | None
231
+ file_size: int | None
232
+ first_seen: str | None
233
+ tags: list[str]
234
+ verdict: Verdict
235
+ next_calls: list[PivotHint]
236
+
237
+
238
+ class PhishingResponse(TypedDict, total=False):
239
+ url: str
240
+ host: str
241
+ is_malicious: bool
242
+ is_stale: bool
243
+ sources: dict[str, Any]
244
+ summary: str
245
+ verdict: Verdict
246
+ next_calls: list[PivotHint]
247
+
248
+
249
+ class PasswordResponse(TypedDict, total=False):
250
+ hash_prefix: str
251
+ found: bool
252
+ pwned_count: int
253
+ summary: str
254
+ verdict: Verdict
255
+ next_calls: list[PivotHint]
256
+
257
+
258
+ # --- Domain ---
259
+
260
+
261
+ class DomainReportResponse(TypedDict, total=False):
262
+ domain: str
263
+ dns: dict[str, Any]
264
+ reverse_dns: dict[str, Any]
265
+ whois: dict[str, Any]
266
+ ssl: dict[str, Any]
267
+ subdomains: dict[str, Any]
268
+ certificates: dict[str, Any]
269
+ email_security: dict[str, Any]
270
+ waf: dict[str, Any]
271
+ threat: dict[str, Any]
272
+ risk: dict[str, Any]
273
+ risk_score: int | None # DEPRECATED (Sunset 2026-09-01); use risk.score
274
+ reputation: dict[str, Any]
275
+ summary: str
276
+ verdict: Verdict
277
+ next_calls: list[PivotHint]
278
+
279
+
280
+ class DnsResponse(TypedDict, total=False):
281
+ domain: str
282
+ records: dict[str, Any]
283
+ summary: str
284
+ verdict: Verdict
285
+ next_calls: list[PivotHint]
286
+
287
+
288
+ class WhoisResponse(TypedDict, total=False):
289
+ domain: str
290
+ whois: dict[str, Any]
291
+ summary: str
292
+ verdict: Verdict
293
+ next_calls: list[PivotHint]
294
+
295
+
296
+ class SubdomainsResponse(TypedDict, total=False):
297
+ domain: str
298
+ count: int
299
+ subdomains: list[str]
300
+ summary: str
301
+ sources: list[str]
302
+ warnings: list[str]
303
+ found_via_wordlist: int
304
+ found_via_crtsh: int
305
+ verdict: Verdict
306
+ next_calls: list[PivotHint]
307
+
308
+
309
+ class CertsResponse(TypedDict, total=False):
310
+ domain: str
311
+ total_certificates: int
312
+ certificates: list[dict[str, Any]]
313
+ summary: str
314
+ verdict: Verdict
315
+ next_calls: list[PivotHint]
316
+
317
+
318
+ class SslResponse(TypedDict, total=False):
319
+ domain: str
320
+ valid: bool
321
+ cert_valid: bool
322
+ grade: str
323
+ validation_errors: list[str]
324
+ issuer: dict[str, Any]
325
+ subject: dict[str, Any]
326
+ san: list[str]
327
+ not_before: str
328
+ not_after: str
329
+ days_until_expiry: int
330
+ summary: str
331
+ verdict: Verdict
332
+ next_calls: list[PivotHint]
333
+
334
+
335
+ class TechResponse(TypedDict, total=False):
336
+ domain: str
337
+ technologies: list[dict[str, Any]]
338
+ categories: dict[str, list[str]]
339
+ count: int
340
+ summary: str
341
+ verdict: Verdict
342
+ next_calls: list[PivotHint]
343
+
344
+
345
+ class ThreatResponse(TypedDict, total=False):
346
+ domain: str
347
+ urlhaus_status: str
348
+ urls_online: int
349
+ url_count: int
350
+ threat_types: list[str]
351
+ tags: list[str]
352
+ urls: list[dict[str, Any]]
353
+ summary: str
354
+ verdict: Verdict
355
+ next_calls: list[PivotHint]
356
+
357
+
358
+ class WaybackResponse(TypedDict, total=False):
359
+ domain: str
360
+ status: str # 'ok' | 'unavailable'
361
+ snapshots: list[dict[str, Any]]
362
+ total_snapshots: int
363
+ earliest: str
364
+ latest: str
365
+ warnings: list[str]
366
+ summary: str
367
+ verdict: Verdict
368
+ next_calls: list[PivotHint]
369
+
370
+
371
+ class AuditResponse(TypedDict, total=False):
372
+ domain: str
373
+ report: DomainReportResponse | None
374
+ headers: dict[str, Any]
375
+ tech: TechResponse | None
376
+ summary: str
377
+ verdict: Verdict
378
+ next_calls: list[PivotHint]
379
+
380
+
381
+ class BulkDomainResponse(TypedDict, total=False):
382
+ results: list[dict[str, Any]]
383
+ total: int
384
+ successful: int
385
+ failed: int
386
+ timed_out: int
387
+ summary: str
388
+ verdict: Verdict
389
+ next_calls: list[PivotHint]
390
+
391
+
392
+ # --- IP / ASN / Threat report ---
393
+
394
+
395
+ class IpLookupResponse(TypedDict, total=False):
396
+ ip: str
397
+ ptr: str | None
398
+ asn: int | None
399
+ asn_name: str | None
400
+ country: str | None
401
+ ports: list[int]
402
+ hostnames: list[str]
403
+ vulns: list[dict[str, Any]]
404
+ cpes: list[str]
405
+ tags: list[str]
406
+ reputation: dict[str, Any]
407
+ cloud_provider: str | None
408
+ is_datacenter: bool
409
+ tor_exit: bool
410
+ risk_score: int
411
+ severity_label: str # low | medium | high | critical
412
+ summary: str
413
+ verdict: Verdict
414
+ next_calls: list[PivotHint]
415
+
416
+
417
+ class AsnResponse(TypedDict, total=False):
418
+ target: str
419
+ resolved_ip: str | None
420
+ asn: int
421
+ asn_name: str
422
+ country: str | None
423
+ prefixes: list[str]
424
+ prefix_count: int
425
+ summary: str
426
+ verdict: Verdict
427
+ next_calls: list[PivotHint]
428
+
429
+
430
+ class ThreatReportResponse(TypedDict, total=False):
431
+ ip: str
432
+ enrichment: dict[str, Any]
433
+ abuseipdb: dict[str, Any]
434
+ shodan: dict[str, Any]
435
+ asn_info: dict[str, Any]
436
+ risk_score: int
437
+ severity_label: str
438
+ summary: str
439
+ verdict: Verdict
440
+ next_calls: list[PivotHint]
441
+
442
+
443
+ # --- Email / Phone / Username ---
444
+
445
+
446
+ class EmailMxResponse(TypedDict, total=False):
447
+ domain: str
448
+ mx_records: list[dict[str, Any]]
449
+ mail_provider: str | None
450
+ email_security: dict[str, Any]
451
+ summary: str
452
+ verdict: Verdict
453
+ next_calls: list[PivotHint]
454
+
455
+
456
+ class DisposableResponse(TypedDict, total=False):
457
+ email: str
458
+ domain: str
459
+ disposable: bool
460
+ provider: str | None
461
+ summary: str
462
+ verdict: Verdict
463
+ next_calls: list[PivotHint]
464
+
465
+
466
+ class PhoneLookupResponse(TypedDict, total=False):
467
+ valid: bool
468
+ number: str
469
+ country: str | None
470
+ region: str | None
471
+ carrier: str | None
472
+ line_type: str | None
473
+ format: dict[str, Any]
474
+ summary: str
475
+ verdict: Verdict
476
+ next_calls: list[PivotHint]
477
+
478
+
479
+ class UsernameLookupResponse(TypedDict, total=False):
480
+ username: str
481
+ found_count: int
482
+ platforms: list[dict[str, Any]]
483
+ summary: str
484
+ verdict: Verdict
485
+ next_calls: list[PivotHint]
486
+
487
+
488
+ # --- ATLAS ---
489
+
490
+
491
+ class AtlasTechniqueResponse(TypedDict, total=False):
492
+ technique_id: str
493
+ name: str
494
+ description: str
495
+ tactics: list[str]
496
+ inherited_tactics: bool
497
+ maturity: str
498
+ attack_reference_id: str
499
+ attack_reference_url: str
500
+ subtechnique_of: str
501
+ created_date: str
502
+ modified_date: str
503
+ verdict: Verdict
504
+ next_calls: list[PivotHint]
505
+
506
+
507
+ class AtlasTechniqueListItem(TypedDict, total=False):
508
+ technique_id: str
509
+ name: str
510
+ description: str
511
+ tactics: list[str]
512
+ inherited_tactics: bool
513
+ maturity: str
514
+ attack_reference_id: str
515
+ subtechnique_of: str
516
+
517
+
518
+ class AtlasTechniqueSearchResponse(TypedDict, total=False):
519
+ query: dict[str, Any]
520
+ total: int
521
+ results: list[AtlasTechniqueListItem]
522
+ verdict: Verdict
523
+ next_calls: list[PivotHint]
524
+
525
+
526
+ class AtlasCaseStudyResponse(TypedDict, total=False):
527
+ case_study_id: str
528
+ name: str
529
+ description: str
530
+ techniques_used: list[str]
531
+ verdict: Verdict
532
+ next_calls: list[PivotHint]
533
+
534
+
535
+ class AtlasCaseStudySearchResponse(TypedDict, total=False):
536
+ query: dict[str, Any]
537
+ total: int
538
+ results: list[AtlasCaseStudyResponse]
539
+ verdict: Verdict
540
+ next_calls: list[PivotHint]
541
+
542
+
543
+ # --- D3FEND ---
544
+
545
+
546
+ class D3fendDefenseResponse(TypedDict, total=False):
547
+ defense_id: str
548
+ label: str
549
+ uri: str
550
+ parent_label: str
551
+ description: str
552
+ tactic: str # singular
553
+ artifact: str
554
+ attack_techniques: list[str]
555
+ verdict: Verdict
556
+ next_calls: list[PivotHint]
557
+
558
+
559
+ class D3fendDefenseListItem(TypedDict, total=False):
560
+ defense_id: str
561
+ label: str
562
+ uri: str
563
+ parent_label: str
564
+ tactic: str
565
+ artifact: str
566
+
567
+
568
+ class D3fendDefenseSearchResponse(TypedDict, total=False):
569
+ query: dict[str, Any]
570
+ total: int
571
+ results: list[D3fendDefenseListItem]
572
+ verdict: Verdict
573
+ next_calls: list[PivotHint]
574
+
575
+
576
+ class D3fendDefenseForAttackItem(TypedDict, total=False):
577
+ defense_id: str
578
+ label: str
579
+ uri: str
580
+ parent_label: str
581
+ tactic: str
582
+ artifact: str
583
+ attack_label: str
584
+ attack_tactic: str
585
+
586
+
587
+ class D3fendForAttackResponse(TypedDict, total=False):
588
+ attack_technique_id: str
589
+ total: int
590
+ truncated: bool
591
+ defenses: list[D3fendDefenseForAttackItem]
592
+ coverage_by_tactic: dict[str, int]
593
+ verdict: Verdict
594
+ next_calls: list[PivotHint]
595
+
596
+
597
+ class D3fendCoverageResponse(TypedDict, total=False):
598
+ queried_techniques: list[str]
599
+ coverage_by_tactic: dict[str, int]
600
+ defended_techniques: list[str]
601
+ undefended_techniques: list[str]
602
+ verdict: Verdict
603
+ next_calls: list[PivotHint]
604
+
605
+
606
+ # --- Code-security checks + scan ---
607
+
608
+
609
+ class CodeCheckResponse(TypedDict, total=False):
610
+ findings: list[dict[str, Any]]
611
+ total: int
612
+ by_severity: dict[str, int]
613
+ summary: str
614
+ verdict: Verdict
615
+ next_calls: list[PivotHint]
616
+
617
+
618
+ class CheckHeadersResponse(TypedDict, total=False):
619
+ findings: list[dict[str, Any]]
620
+ total: int
621
+ by_severity: dict[str, int]
622
+ summary: str
623
+ verdict: Verdict
624
+ next_calls: list[PivotHint]
625
+
626
+
627
+ class ScanHeadersResponse(TypedDict, total=False):
628
+ domain: str
629
+ status_code: int
630
+ url: str
631
+ score: int
632
+ grade: str
633
+ findings: list[dict[str, Any]]
634
+ headers: dict[str, str]
635
+ summary: str
636
+ verdict: Verdict
637
+ next_calls: list[PivotHint]
638
+
639
+
640
+ class DependenciesResponse(TypedDict, total=False):
641
+ findings: list[dict[str, Any]]
642
+ total: int
643
+ by_severity: dict[str, int]
644
+ summary: str
645
+ verdict: Verdict
646
+ next_calls: list[PivotHint]
647
+
648
+
649
+ # --- Meta ---
650
+
651
+
652
+ class StatusResponse(TypedDict, total=False):
653
+ status: str
654
+ version: str
655
+ uptime_seconds: int
656
+
657
+
658
+ class UsageResponse(TypedDict, total=False):
659
+ requests_remaining: int
660
+ window_seconds: int
661
+ tier: str
File without changes