cloudg 0.3.0__tar.gz → 0.3.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.
Files changed (87) hide show
  1. {cloudg-0.3.0 → cloudg-0.3.2}/PKG-INFO +3 -6
  2. {cloudg-0.3.0 → cloudg-0.3.2}/README.md +2 -5
  3. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/__init__.py +1 -1
  4. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/api.py +59 -8
  5. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/cli.py +260 -248
  6. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/normaliser.py +194 -34
  7. cloudg-0.3.2/cloudg/rules/check_equivalence.yaml +67 -0
  8. cloudg-0.3.2/cloudg/ui.py +255 -0
  9. {cloudg-0.3.0 → cloudg-0.3.2}/pyproject.toml +1 -1
  10. {cloudg-0.3.0 → cloudg-0.3.2}/tests/test_normaliser.py +106 -0
  11. {cloudg-0.3.0 → cloudg-0.3.2}/tests/test_scanner_orchestration.py +40 -0
  12. {cloudg-0.3.0 → cloudg-0.3.2}/.gitignore +0 -0
  13. {cloudg-0.3.0 → cloudg-0.3.2}/LICENSE +0 -0
  14. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/collectors/__init__.py +0 -0
  15. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/collectors/aws.py +0 -0
  16. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/collectors/azure.py +0 -0
  17. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/collectors/base.py +0 -0
  18. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/collectors/gcp.py +0 -0
  19. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/collectors/multi.py +0 -0
  20. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/config.py +0 -0
  21. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/coverage.py +0 -0
  22. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/credentials.py +0 -0
  23. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/graph/__init__.py +0 -0
  24. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/graph/builder.py +0 -0
  25. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/graph/ontology.py +0 -0
  26. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/graph/rag_export.py +0 -0
  27. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/graph/reachability.py +0 -0
  28. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/policies/custodian-aws-security.yml +0 -0
  29. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/policies/custodian-azure.yml +0 -0
  30. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/policies/custodian-gcp.yml +0 -0
  31. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/policies/custodian.yml +0 -0
  32. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/region_discovery.py +0 -0
  33. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/registry.py +0 -0
  34. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/renderers/__init__.py +0 -0
  35. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/renderers/html_report.py +0 -0
  36. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/renderers/json_export.py +0 -0
  37. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/renderers/svg.py +0 -0
  38. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/renderers/terraform_export.py +0 -0
  39. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/retry.py +0 -0
  40. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/cis_aws_v3.yaml +0 -0
  41. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/cis_azure_v2.yaml +0 -0
  42. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/cis_gcp_v3.yaml +0 -0
  43. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/aws_foundational_security_best_practices_aws.yaml +0 -0
  44. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/cis_5.0_aws.yaml +0 -0
  45. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/cis_5.0_azure.yaml +0 -0
  46. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/cis_5.0_gcp.yaml +0 -0
  47. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/gdpr_aws.yaml +0 -0
  48. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/hipaa_aws.yaml +0 -0
  49. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/hipaa_azure.yaml +0 -0
  50. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/hipaa_gcp.yaml +0 -0
  51. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/iso27001_2022_aws.yaml +0 -0
  52. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/iso27001_2022_azure.yaml +0 -0
  53. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/iso27001_2022_gcp.yaml +0 -0
  54. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/mitre_attack_aws.yaml +0 -0
  55. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/mitre_attack_azure.yaml +0 -0
  56. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/mitre_attack_gcp.yaml +0 -0
  57. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/nist_800_53_revision_5_aws.yaml +0 -0
  58. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/nist_csf_2.0_aws.yaml +0 -0
  59. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/pci_4.0_aws.yaml +0 -0
  60. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/pci_4.0_azure.yaml +0 -0
  61. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/pci_4.0_gcp.yaml +0 -0
  62. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/soc2_aws.yaml +0 -0
  63. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/soc2_azure.yaml +0 -0
  64. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/frameworks/soc2_gcp.yaml +0 -0
  65. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/gdpr.yaml +0 -0
  66. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/hipaa_security_rule.yaml +0 -0
  67. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/iso_27001_2022.yaml +0 -0
  68. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/nist_800_53.yaml +0 -0
  69. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/pci_dss_v4.yaml +0 -0
  70. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/rules/soc2_tsc.yaml +0 -0
  71. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/scanners/__init__.py +0 -0
  72. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/scanners/checkov.py +0 -0
  73. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/scanners/iam_linter.py +0 -0
  74. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/scanners/prowler.py +0 -0
  75. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/scanners/scoutsuite.py +0 -0
  76. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/scanners/trivy.py +0 -0
  77. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/schema/__init__.py +0 -0
  78. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/schema/models.py +0 -0
  79. {cloudg-0.3.0 → cloudg-0.3.2}/cloudg/templates/report.html.j2 +0 -0
  80. {cloudg-0.3.0 → cloudg-0.3.2}/tests/__init__.py +0 -0
  81. {cloudg-0.3.0 → cloudg-0.3.2}/tests/conftest.py +0 -0
  82. {cloudg-0.3.0 → cloudg-0.3.2}/tests/test_api.py +0 -0
  83. {cloudg-0.3.0 → cloudg-0.3.2}/tests/test_collectors.py +0 -0
  84. {cloudg-0.3.0 → cloudg-0.3.2}/tests/test_ontology.py +0 -0
  85. {cloudg-0.3.0 → cloudg-0.3.2}/tests/test_rag_export.py +0 -0
  86. {cloudg-0.3.0 → cloudg-0.3.2}/tests/test_region_discovery.py +0 -0
  87. {cloudg-0.3.0 → cloudg-0.3.2}/tests/test_terraform_export.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cloudg
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: cloudg (cloud graphing) — multi-cloud infrastructure mapping, knowledge graphs and security intelligence for AWS, Azure and GCP
5
5
  Project-URL: Homepage, https://github.com/morpheuslord/cloudg
6
6
  Project-URL: Repository, https://github.com/morpheuslord/cloudg
@@ -75,7 +75,7 @@ Description-Content-Type: text/markdown
75
75
  <p align="center">
76
76
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License: MIT"></a>
77
77
  <img src="https://img.shields.io/badge/python-3.11%2B-blue.svg" alt="Python 3.11+">
78
- <img src="https://img.shields.io/badge/version-0.3.0-4c1.svg" alt="Version 0.3.0">
78
+ <img src="https://img.shields.io/badge/version-0.3.1-4c1.svg" alt="Version 0.3.1">
79
79
  <a href="https://github.com/astral-sh/uv"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json" alt="uv"></a>
80
80
  </p>
81
81
 
@@ -194,7 +194,7 @@ Collection runs all providers concurrently with asyncio, iterating accounts and
194
194
 
195
195
  The same inventory feeds three other exports. The ontology module infers about 62 typed relations (`exposed_to_internet`, `assumes_role`, `encrypted_by`, `hosted_in_vpc` and so on) and writes RDF you can query with SPARQL. The RAG exporter chunks the graph three ways (per asset, per Louvain community, per relation domain) into JSONL for retrieval pipelines. The Terraform exporter maps 25+ asset types to `.tf.json` resources with an `import.sh` to adopt them into state.
196
196
 
197
- Scanner findings are deduplicated by resource and title, rescored against CVSS, and mapped to compliance controls.
197
+ Scanner findings are deduplicated in two passes — within a scanner by (scanner, check ID, resource), and across scanners only when both the normalised title and the underlying check semantics (`rules/check_equivalence.yaml`) match — then rescored against CVSS and mapped to compliance controls.
198
198
 
199
199
  ---
200
200
 
@@ -325,6 +325,3 @@ Python 3.11 or newer. The moto/aiobotocore incompatibility around async response
325
325
 
326
326
  ---
327
327
 
328
- ## License
329
-
330
- MIT
@@ -5,7 +5,7 @@
5
5
  <p align="center">
6
6
  <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg" alt="License: MIT"></a>
7
7
  <img src="https://img.shields.io/badge/python-3.11%2B-blue.svg" alt="Python 3.11+">
8
- <img src="https://img.shields.io/badge/version-0.3.0-4c1.svg" alt="Version 0.3.0">
8
+ <img src="https://img.shields.io/badge/version-0.3.1-4c1.svg" alt="Version 0.3.1">
9
9
  <a href="https://github.com/astral-sh/uv"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json" alt="uv"></a>
10
10
  </p>
11
11
 
@@ -124,7 +124,7 @@ Collection runs all providers concurrently with asyncio, iterating accounts and
124
124
 
125
125
  The same inventory feeds three other exports. The ontology module infers about 62 typed relations (`exposed_to_internet`, `assumes_role`, `encrypted_by`, `hosted_in_vpc` and so on) and writes RDF you can query with SPARQL. The RAG exporter chunks the graph three ways (per asset, per Louvain community, per relation domain) into JSONL for retrieval pipelines. The Terraform exporter maps 25+ asset types to `.tf.json` resources with an `import.sh` to adopt them into state.
126
126
 
127
- Scanner findings are deduplicated by resource and title, rescored against CVSS, and mapped to compliance controls.
127
+ Scanner findings are deduplicated in two passes — within a scanner by (scanner, check ID, resource), and across scanners only when both the normalised title and the underlying check semantics (`rules/check_equivalence.yaml`) match — then rescored against CVSS and mapped to compliance controls.
128
128
 
129
129
  ---
130
130
 
@@ -255,6 +255,3 @@ Python 3.11 or newer. The moto/aiobotocore incompatibility around async response
255
255
 
256
256
  ---
257
257
 
258
- ## License
259
-
260
- MIT
@@ -1,6 +1,6 @@
1
1
  """cloudg — cloud graphing: multi-cloud mapping and security intelligence."""
2
2
 
3
- __version__ = "0.3.0"
3
+ __version__ = "0.3.2"
4
4
 
5
5
  from cloudg.api import (
6
6
  AnalysisResult,
@@ -43,6 +43,34 @@ from cloudg.schema.models import (
43
43
  logger = logging.getLogger(__name__)
44
44
 
45
45
 
46
+ def resolve_iac_dirs(
47
+ iac_dir: str | None,
48
+ config_dirs: list[str] | None,
49
+ terraform_dir: str | Path | None = None,
50
+ ) -> tuple[list[str], str | None]:
51
+ """Resolve which directories the IaC scanners (Checkov, Trivy fs) target.
52
+
53
+ Priority: explicit dir -> configured dirs -> the generated Terraform
54
+ recreation of the live infrastructure. There is deliberately no fallback
55
+ to "." any more: scanning whatever directory cloudg happens to run from
56
+ is not a scan of the cloud, and its zero findings read like a clean bill
57
+ of health.
58
+
59
+ Returns:
60
+ (dirs, source) where source is "cli", "config", "terraform", or
61
+ None when there is nothing for the IaC scanners to target.
62
+ """
63
+ if iac_dir:
64
+ return [iac_dir], "cli"
65
+ if config_dirs:
66
+ return list(config_dirs), "config"
67
+ if terraform_dir:
68
+ tf_path = Path(terraform_dir)
69
+ if tf_path.is_dir() and any(tf_path.glob("*.tf.json")):
70
+ return [str(tf_path)], "terraform"
71
+ return [], None
72
+
73
+
46
74
  # ---------------------------------------------------------------------------
47
75
  # Result dataclasses
48
76
  # ---------------------------------------------------------------------------
@@ -285,14 +313,37 @@ class CloudGEngine:
285
313
  logger.error("Graph analysis failed: %s", exc)
286
314
  self._emit_error("graph_analysis", exc)
287
315
 
288
- # Resolve IaC directories
289
- resolved_iac_dirs: list[str] = []
290
- if iac_dir:
291
- resolved_iac_dirs = [iac_dir]
292
- elif self.config.scanners.iac_directories:
293
- resolved_iac_dirs = list(self.config.scanners.iac_directories)
294
- else:
295
- resolved_iac_dirs = ["."]
316
+ # Resolve IaC directories. When nothing is configured but Terraform
317
+ # recreation is enabled, generate it now and scan that: the IaC
318
+ # scanners then audit the live infrastructure via its Terraform
319
+ # representation instead of an unrelated local directory.
320
+ tf_dir: str | None = None
321
+ if (
322
+ not iac_dir
323
+ and not self.config.scanners.iac_directories
324
+ and self.config.terraform.enabled
325
+ ):
326
+ tf_dir = self.config.terraform.output_dir or str(out / "terraform")
327
+ try:
328
+ from cloudg.renderers.terraform_export import TerraformExporter
329
+
330
+ TerraformExporter(output_dir=tf_dir).export(assets, edges)
331
+ except Exception as exc:
332
+ logger.error("Terraform export for IaC scanning failed: %s", exc)
333
+ self._emit_error("terraform", exc)
334
+ tf_dir = None
335
+
336
+ resolved_iac_dirs, iac_source = resolve_iac_dirs(
337
+ iac_dir, self.config.scanners.iac_directories, tf_dir
338
+ )
339
+ if iac_source == "terraform":
340
+ logger.info("IaC scanners target the Terraform recreation at %s", resolved_iac_dirs[0])
341
+ elif not resolved_iac_dirs and "checkov" in scanner_list:
342
+ logger.warning(
343
+ "Skipping Checkov: no IaC directory configured. "
344
+ "Pass iac_dir, set scanners.iac_directories, or enable terraform "
345
+ "so the recreated infrastructure can be scanned."
346
+ )
296
347
 
297
348
  # Resolve images
298
349
  resolved_images: list[str] = images or list(self.config.scanners.trivy_images)