vpcatlas 0.4.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 (231) hide show
  1. vpcatlas-0.4.2/.gitattributes +3 -0
  2. vpcatlas-0.4.2/.github/CODEOWNERS +2 -0
  3. vpcatlas-0.4.2/.github/PULL_REQUEST_TEMPLATE.md +15 -0
  4. vpcatlas-0.4.2/.github/actions/setup/action.yml +29 -0
  5. vpcatlas-0.4.2/.github/dependabot.yml +43 -0
  6. vpcatlas-0.4.2/.github/workflows/ci.yml +552 -0
  7. vpcatlas-0.4.2/.github/workflows/codeql.yml +46 -0
  8. vpcatlas-0.4.2/.github/workflows/nightly.yml +174 -0
  9. vpcatlas-0.4.2/.github/workflows/release.yml +649 -0
  10. vpcatlas-0.4.2/.github/workflows/scorecard.yml +56 -0
  11. vpcatlas-0.4.2/.gitignore +24 -0
  12. vpcatlas-0.4.2/.pre-commit-config.yaml +7 -0
  13. vpcatlas-0.4.2/CHANGELOG.md +228 -0
  14. vpcatlas-0.4.2/CODE_OF_CONDUCT.md +106 -0
  15. vpcatlas-0.4.2/CONTRIBUTING.md +168 -0
  16. vpcatlas-0.4.2/LICENSE +21 -0
  17. vpcatlas-0.4.2/Makefile +52 -0
  18. vpcatlas-0.4.2/PKG-INFO +350 -0
  19. vpcatlas-0.4.2/README.md +316 -0
  20. vpcatlas-0.4.2/SECURITY.md +54 -0
  21. vpcatlas-0.4.2/demo/README.md +106 -0
  22. vpcatlas-0.4.2/demo/fake_aws.py +1149 -0
  23. vpcatlas-0.4.2/demo/vpcatlas.tape +47 -0
  24. vpcatlas-0.4.2/deploy/Dockerfile +75 -0
  25. vpcatlas-0.4.2/deploy/Dockerfile.dockerignore +25 -0
  26. vpcatlas-0.4.2/deploy/Dockerfile.lambda +45 -0
  27. vpcatlas-0.4.2/deploy/Dockerfile.lambda.dockerignore +25 -0
  28. vpcatlas-0.4.2/deploy/docker-compose.yml +59 -0
  29. vpcatlas-0.4.2/deploy/ecs/README.md +57 -0
  30. vpcatlas-0.4.2/deploy/ecs/task-definition.json +60 -0
  31. vpcatlas-0.4.2/deploy/iam/auditor-policy.json +264 -0
  32. vpcatlas-0.4.2/deploy/kubernetes/deployment.yaml +96 -0
  33. vpcatlas-0.4.2/deploy/kubernetes/service.yaml +18 -0
  34. vpcatlas-0.4.2/deploy/kubernetes/serviceaccount.yaml +14 -0
  35. vpcatlas-0.4.2/deploy/lambda/README.md +102 -0
  36. vpcatlas-0.4.2/docs/architecture.md +59 -0
  37. vpcatlas-0.4.2/docs/assets/dashboard.png +0 -0
  38. vpcatlas-0.4.2/docs/assets/demo.gif +0 -0
  39. vpcatlas-0.4.2/docs/assets/region-eu-west-1.png +0 -0
  40. vpcatlas-0.4.2/docs/assets/vpc-prod.png +0 -0
  41. vpcatlas-0.4.2/docs/changelog.md +234 -0
  42. vpcatlas-0.4.2/docs/cli/diagram.md +100 -0
  43. vpcatlas-0.4.2/docs/cli/discover.md +106 -0
  44. vpcatlas-0.4.2/docs/cli/exit-codes.md +23 -0
  45. vpcatlas-0.4.2/docs/cli/iam-policy.md +81 -0
  46. vpcatlas-0.4.2/docs/cli/index.md +130 -0
  47. vpcatlas-0.4.2/docs/cli/inventory.md +94 -0
  48. vpcatlas-0.4.2/docs/cli/serve.md +140 -0
  49. vpcatlas-0.4.2/docs/configuration.md +173 -0
  50. vpcatlas-0.4.2/docs/contributing.md +194 -0
  51. vpcatlas-0.4.2/docs/coverage.md +161 -0
  52. vpcatlas-0.4.2/docs/credentials.md +359 -0
  53. vpcatlas-0.4.2/docs/cross-account.md +138 -0
  54. vpcatlas-0.4.2/docs/dashboard.md +254 -0
  55. vpcatlas-0.4.2/docs/deploy/compose.md +71 -0
  56. vpcatlas-0.4.2/docs/deploy/docker.md +141 -0
  57. vpcatlas-0.4.2/docs/deploy/ecs.md +97 -0
  58. vpcatlas-0.4.2/docs/deploy/kubernetes.md +115 -0
  59. vpcatlas-0.4.2/docs/deploy/lambda.md +134 -0
  60. vpcatlas-0.4.2/docs/deploy.md +25 -0
  61. vpcatlas-0.4.2/docs/diagram-quality.md +348 -0
  62. vpcatlas-0.4.2/docs/diagram-style.md +1062 -0
  63. vpcatlas-0.4.2/docs/engineering.md +170 -0
  64. vpcatlas-0.4.2/docs/faq.md +104 -0
  65. vpcatlas-0.4.2/docs/iam.md +713 -0
  66. vpcatlas-0.4.2/docs/index.md +84 -0
  67. vpcatlas-0.4.2/docs/install.md +408 -0
  68. vpcatlas-0.4.2/docs/landscape.md +92 -0
  69. vpcatlas-0.4.2/docs/limitations.md +157 -0
  70. vpcatlas-0.4.2/docs/model.md +161 -0
  71. vpcatlas-0.4.2/docs/outputs/diagrams.md +130 -0
  72. vpcatlas-0.4.2/docs/outputs/graph.md +112 -0
  73. vpcatlas-0.4.2/docs/outputs/inventory.md +198 -0
  74. vpcatlas-0.4.2/docs/quickstart.md +140 -0
  75. vpcatlas-0.4.2/docs/troubleshooting.md +260 -0
  76. vpcatlas-0.4.2/mkdocs.yml +79 -0
  77. vpcatlas-0.4.2/packaging/pyinstaller/entry.py +8 -0
  78. vpcatlas-0.4.2/packaging/pyinstaller/vpcatlas.spec +97 -0
  79. vpcatlas-0.4.2/pyproject.toml +56 -0
  80. vpcatlas-0.4.2/requirements-build.lock +69 -0
  81. vpcatlas-0.4.2/requirements-release.lock +522 -0
  82. vpcatlas-0.4.2/requirements.lock +883 -0
  83. vpcatlas-0.4.2/scripts/bench_layout.py +126 -0
  84. vpcatlas-0.4.2/scripts/build-binary.sh +147 -0
  85. vpcatlas-0.4.2/scripts/demo.sh +153 -0
  86. vpcatlas-0.4.2/scripts/install.ps1 +331 -0
  87. vpcatlas-0.4.2/scripts/install.sh +214 -0
  88. vpcatlas-0.4.2/scripts/lock.sh +38 -0
  89. vpcatlas-0.4.2/scripts/publish-docs.sh +15 -0
  90. vpcatlas-0.4.2/scripts/requirements-build.in +4 -0
  91. vpcatlas-0.4.2/scripts/requirements-release.in +5 -0
  92. vpcatlas-0.4.2/src/vpcatlas/__init__.py +3 -0
  93. vpcatlas-0.4.2/src/vpcatlas/__main__.py +8 -0
  94. vpcatlas-0.4.2/src/vpcatlas/auditor-policy.json +264 -0
  95. vpcatlas-0.4.2/src/vpcatlas/cli.py +1054 -0
  96. vpcatlas-0.4.2/src/vpcatlas/collectors/__init__.py +22 -0
  97. vpcatlas-0.4.2/src/vpcatlas/collectors/_dns_targets.py +321 -0
  98. vpcatlas-0.4.2/src/vpcatlas/collectors/account_services.py +379 -0
  99. vpcatlas-0.4.2/src/vpcatlas/collectors/acm.py +94 -0
  100. vpcatlas-0.4.2/src/vpcatlas/collectors/apigw.py +350 -0
  101. vpcatlas-0.4.2/src/vpcatlas/collectors/base.py +465 -0
  102. vpcatlas-0.4.2/src/vpcatlas/collectors/cloudfront.py +107 -0
  103. vpcatlas-0.4.2/src/vpcatlas/collectors/discover.py +333 -0
  104. vpcatlas-0.4.2/src/vpcatlas/collectors/dynamodb.py +41 -0
  105. vpcatlas-0.4.2/src/vpcatlas/collectors/ec2_instances.py +191 -0
  106. vpcatlas-0.4.2/src/vpcatlas/collectors/ec2_peering.py +55 -0
  107. vpcatlas-0.4.2/src/vpcatlas/collectors/ec2_tgw.py +236 -0
  108. vpcatlas-0.4.2/src/vpcatlas/collectors/ec2_vpc.py +499 -0
  109. vpcatlas-0.4.2/src/vpcatlas/collectors/ecs.py +151 -0
  110. vpcatlas-0.4.2/src/vpcatlas/collectors/efs.py +66 -0
  111. vpcatlas-0.4.2/src/vpcatlas/collectors/eks.py +106 -0
  112. vpcatlas-0.4.2/src/vpcatlas/collectors/elasticache.py +159 -0
  113. vpcatlas-0.4.2/src/vpcatlas/collectors/elb.py +107 -0
  114. vpcatlas-0.4.2/src/vpcatlas/collectors/endpoints.py +126 -0
  115. vpcatlas-0.4.2/src/vpcatlas/collectors/lambda_fn.py +120 -0
  116. vpcatlas-0.4.2/src/vpcatlas/collectors/linker.py +1044 -0
  117. vpcatlas-0.4.2/src/vpcatlas/collectors/network_firewall.py +102 -0
  118. vpcatlas-0.4.2/src/vpcatlas/collectors/prefix_lists.py +48 -0
  119. vpcatlas-0.4.2/src/vpcatlas/collectors/ram.py +104 -0
  120. vpcatlas-0.4.2/src/vpcatlas/collectors/rds.py +167 -0
  121. vpcatlas-0.4.2/src/vpcatlas/collectors/route53.py +183 -0
  122. vpcatlas-0.4.2/src/vpcatlas/collectors/route53_resolver.py +90 -0
  123. vpcatlas-0.4.2/src/vpcatlas/collectors/s3.py +182 -0
  124. vpcatlas-0.4.2/src/vpcatlas/collectors/session.py +162 -0
  125. vpcatlas-0.4.2/src/vpcatlas/collectors/shield.py +65 -0
  126. vpcatlas-0.4.2/src/vpcatlas/collectors/targets.py +159 -0
  127. vpcatlas-0.4.2/src/vpcatlas/collectors/vpn_dx.py +262 -0
  128. vpcatlas-0.4.2/src/vpcatlas/collectors/wafv2.py +155 -0
  129. vpcatlas-0.4.2/src/vpcatlas/dashboard/__init__.py +6 -0
  130. vpcatlas-0.4.2/src/vpcatlas/dashboard/app.py +724 -0
  131. vpcatlas-0.4.2/src/vpcatlas/dashboard/healthcheck.py +23 -0
  132. vpcatlas-0.4.2/src/vpcatlas/dashboard/history.py +232 -0
  133. vpcatlas-0.4.2/src/vpcatlas/dashboard/lambda_handler.py +62 -0
  134. vpcatlas-0.4.2/src/vpcatlas/dashboard/scan.py +304 -0
  135. vpcatlas-0.4.2/src/vpcatlas/dashboard/security.py +197 -0
  136. vpcatlas-0.4.2/src/vpcatlas/dashboard/service.py +1038 -0
  137. vpcatlas-0.4.2/src/vpcatlas/dashboard/settings.py +245 -0
  138. vpcatlas-0.4.2/src/vpcatlas/dashboard/snapshot.py +207 -0
  139. vpcatlas-0.4.2/src/vpcatlas/dashboard/static/app.css +261 -0
  140. vpcatlas-0.4.2/src/vpcatlas/dashboard/static/app.js +575 -0
  141. vpcatlas-0.4.2/src/vpcatlas/dashboard/static/vendor/NOTICE.txt +15 -0
  142. vpcatlas-0.4.2/src/vpcatlas/dashboard/static/vendor/viewer-static.min.js +9176 -0
  143. vpcatlas-0.4.2/src/vpcatlas/dashboard/store.py +392 -0
  144. vpcatlas-0.4.2/src/vpcatlas/dashboard/templates/base.html +20 -0
  145. vpcatlas-0.4.2/src/vpcatlas/dashboard/templates/diff.html +181 -0
  146. vpcatlas-0.4.2/src/vpcatlas/dashboard/templates/history.html +410 -0
  147. vpcatlas-0.4.2/src/vpcatlas/dashboard/templates/index.html +150 -0
  148. vpcatlas-0.4.2/src/vpcatlas/exporters/__init__.py +105 -0
  149. vpcatlas-0.4.2/src/vpcatlas/exporters/behind.py +176 -0
  150. vpcatlas-0.4.2/src/vpcatlas/exporters/drawio.py +623 -0
  151. vpcatlas-0.4.2/src/vpcatlas/exporters/families.py +94 -0
  152. vpcatlas-0.4.2/src/vpcatlas/exporters/graphview.py +504 -0
  153. vpcatlas-0.4.2/src/vpcatlas/exporters/layout.py +3988 -0
  154. vpcatlas-0.4.2/src/vpcatlas/exporters/lucid.py +573 -0
  155. vpcatlas-0.4.2/src/vpcatlas/exporters/lucid_classes.json +38 -0
  156. vpcatlas-0.4.2/src/vpcatlas/exporters/registry.py +1080 -0
  157. vpcatlas-0.4.2/src/vpcatlas/exporters/service_rows.py +762 -0
  158. vpcatlas-0.4.2/src/vpcatlas/exporters/vpc_level.py +1038 -0
  159. vpcatlas-0.4.2/src/vpcatlas/inventory/__init__.py +27 -0
  160. vpcatlas-0.4.2/src/vpcatlas/inventory/build.py +816 -0
  161. vpcatlas-0.4.2/src/vpcatlas/inventory/render.py +541 -0
  162. vpcatlas-0.4.2/src/vpcatlas/inventory/schema.py +147 -0
  163. vpcatlas-0.4.2/src/vpcatlas/model/__init__.py +155 -0
  164. vpcatlas-0.4.2/src/vpcatlas/model/diff.py +321 -0
  165. vpcatlas-0.4.2/src/vpcatlas/model/graph.py +399 -0
  166. vpcatlas-0.4.2/src/vpcatlas/model/redact.py +57 -0
  167. vpcatlas-0.4.2/src/vpcatlas/model/resources.py +1188 -0
  168. vpcatlas-0.4.2/src/vpcatlas/services.py +131 -0
  169. vpcatlas-0.4.2/tests/atlas_fixtures.py +184 -0
  170. vpcatlas-0.4.2/tests/compute_fakes.py +111 -0
  171. vpcatlas-0.4.2/tests/conftest.py +67 -0
  172. vpcatlas-0.4.2/tests/data_collectors_fakes.py +82 -0
  173. vpcatlas-0.4.2/tests/fixtures/__init__.py +0 -0
  174. vpcatlas-0.4.2/tests/fixtures/fanout.py +462 -0
  175. vpcatlas-0.4.2/tests/fixtures/malz.py +1370 -0
  176. vpcatlas-0.4.2/tests/test_cli.py +203 -0
  177. vpcatlas-0.4.2/tests/test_cli_options.py +70 -0
  178. vpcatlas-0.4.2/tests/test_collector_dynamodb.py +48 -0
  179. vpcatlas-0.4.2/tests/test_collector_efs.py +92 -0
  180. vpcatlas-0.4.2/tests/test_collector_elasticache.py +93 -0
  181. vpcatlas-0.4.2/tests/test_collector_rds.py +124 -0
  182. vpcatlas-0.4.2/tests/test_collector_s3.py +167 -0
  183. vpcatlas-0.4.2/tests/test_collectors.py +57 -0
  184. vpcatlas-0.4.2/tests/test_collectors_account_services.py +518 -0
  185. vpcatlas-0.4.2/tests/test_collectors_acm.py +122 -0
  186. vpcatlas-0.4.2/tests/test_collectors_apigw.py +244 -0
  187. vpcatlas-0.4.2/tests/test_collectors_cloudfront.py +159 -0
  188. vpcatlas-0.4.2/tests/test_collectors_ec2_instances.py +169 -0
  189. vpcatlas-0.4.2/tests/test_collectors_ecs.py +121 -0
  190. vpcatlas-0.4.2/tests/test_collectors_eks.py +102 -0
  191. vpcatlas-0.4.2/tests/test_collectors_lambda_fn.py +111 -0
  192. vpcatlas-0.4.2/tests/test_collectors_privacy.py +295 -0
  193. vpcatlas-0.4.2/tests/test_collectors_registry.py +51 -0
  194. vpcatlas-0.4.2/tests/test_collectors_route53.py +338 -0
  195. vpcatlas-0.4.2/tests/test_collectors_shield.py +85 -0
  196. vpcatlas-0.4.2/tests/test_collectors_targets.py +164 -0
  197. vpcatlas-0.4.2/tests/test_collectors_wafv2.py +158 -0
  198. vpcatlas-0.4.2/tests/test_credentials.py +613 -0
  199. vpcatlas-0.4.2/tests/test_dashboard.py +1021 -0
  200. vpcatlas-0.4.2/tests/test_dashboard_drawing.py +314 -0
  201. vpcatlas-0.4.2/tests/test_dashboard_families.py +179 -0
  202. vpcatlas-0.4.2/tests/test_dashboard_hardening.py +495 -0
  203. vpcatlas-0.4.2/tests/test_dashboard_lazy.py +218 -0
  204. vpcatlas-0.4.2/tests/test_diff.py +339 -0
  205. vpcatlas-0.4.2/tests/test_diff_highlight.py +318 -0
  206. vpcatlas-0.4.2/tests/test_drawio_layers.py +193 -0
  207. vpcatlas-0.4.2/tests/test_exporters.py +81 -0
  208. vpcatlas-0.4.2/tests/test_exporters_style.py +1126 -0
  209. vpcatlas-0.4.2/tests/test_fan_trunk.py +418 -0
  210. vpcatlas-0.4.2/tests/test_fanout_fixture.py +151 -0
  211. vpcatlas-0.4.2/tests/test_global_overlap.py +59 -0
  212. vpcatlas-0.4.2/tests/test_history_store.py +426 -0
  213. vpcatlas-0.4.2/tests/test_iam_policy_matches_collectors.py +181 -0
  214. vpcatlas-0.4.2/tests/test_install_ps1.py +129 -0
  215. vpcatlas-0.4.2/tests/test_install_sh.py +188 -0
  216. vpcatlas-0.4.2/tests/test_installer_release_lookup.py +19 -0
  217. vpcatlas-0.4.2/tests/test_inventory.py +184 -0
  218. vpcatlas-0.4.2/tests/test_inventory_render.py +263 -0
  219. vpcatlas-0.4.2/tests/test_inventory_services.py +116 -0
  220. vpcatlas-0.4.2/tests/test_layout_fold.py +205 -0
  221. vpcatlas-0.4.2/tests/test_layout_quality.py +1772 -0
  222. vpcatlas-0.4.2/tests/test_linker_services.py +366 -0
  223. vpcatlas-0.4.2/tests/test_malz_fixture.py +451 -0
  224. vpcatlas-0.4.2/tests/test_model_external_accounts.py +110 -0
  225. vpcatlas-0.4.2/tests/test_model_services.py +250 -0
  226. vpcatlas-0.4.2/tests/test_operator_identity_leaks.py +73 -0
  227. vpcatlas-0.4.2/tests/test_route_budget.py +303 -0
  228. vpcatlas-0.4.2/tests/test_service_filters.py +335 -0
  229. vpcatlas-0.4.2/tests/test_startup.py +120 -0
  230. vpcatlas-0.4.2/tests/test_supply_chain.py +114 -0
  231. vpcatlas-0.4.2/tests/test_vpc_level.py +433 -0
@@ -0,0 +1,3 @@
1
+ # Every text file is checked out with LF on every system: the bundled viewer is checked against its
2
+ # recorded size and hash, and the exported diagrams are compared byte for byte with the served ones.
3
+ * text=auto eol=lf
@@ -0,0 +1,2 @@
1
+ # Every path is reviewed by the maintainer.
2
+ * @karina-pp
@@ -0,0 +1,15 @@
1
+ ## What and why
2
+
3
+ <!-- What does this change, and what problem does it solve? Link the issue if there is one. -->
4
+
5
+ ## How it was tested
6
+
7
+ <!-- Commands you ran (make lint test, pytest -k ..., a scan against a fixture, a build) and what you saw. -->
8
+
9
+ ## Checklist
10
+
11
+ - [ ] Tests added or updated, and `make lint test` passes.
12
+ - [ ] Documentation updated (README, the matching page in `docs/`) if a command, route, permission or output changed.
13
+ - [ ] `CHANGELOG.md` has an entry under `## [Unreleased]`.
14
+ - [ ] No secrets, real account ids, ARNs or customer names in the diff or in the fixtures (use `111111111111`-style ids).
15
+ - [ ] A new AWS call is read-only and is listed in the auditor policy, in Coverage and in IAM.
@@ -0,0 +1,29 @@
1
+ name: Set up Python and install vpcatlas
2
+ description: Python with a pip cache keyed on pyproject.toml, then vpcatlas installed in editable mode with the chosen extras. The caller checks the code out first.
3
+
4
+ inputs:
5
+ python-version:
6
+ description: Python version to install.
7
+ required: false
8
+ default: "3.12"
9
+ extras:
10
+ description: Extras of vpcatlas to install, comma separated, for example dev or dev,docs.
11
+ required: false
12
+ default: dev
13
+
14
+ runs:
15
+ using: composite
16
+ steps:
17
+ - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
18
+ with:
19
+ python-version: ${{ inputs.python-version }}
20
+ cache: pip
21
+ cache-dependency-path: pyproject.toml
22
+
23
+ - name: Install vpcatlas
24
+ shell: bash
25
+ env:
26
+ EXTRAS: ${{ inputs.extras }}
27
+ run: |
28
+ python -m pip install --upgrade pip
29
+ python -m pip install -e ".[${EXTRAS}]"
@@ -0,0 +1,43 @@
1
+ # Weekly update pull requests. Actions and the Docker base images are pinned to digests or commit SHAs, so
2
+ # Dependabot is what keeps them current; each bump arrives as a reviewable diff with the upstream release notes.
3
+ version: 2
4
+
5
+ updates:
6
+ - package-ecosystem: github-actions
7
+ directory: /
8
+ schedule:
9
+ interval: weekly
10
+ day: monday
11
+ cooldown:
12
+ default-days: 7
13
+ open-pull-requests-limit: 5
14
+ commit-message:
15
+ prefix: ci(deps)
16
+ groups:
17
+ actions:
18
+ patterns: ["*"]
19
+
20
+ - package-ecosystem: pip
21
+ directory: /
22
+ schedule:
23
+ interval: weekly
24
+ day: monday
25
+ cooldown:
26
+ default-days: 7
27
+ open-pull-requests-limit: 5
28
+ commit-message:
29
+ prefix: build(deps)
30
+ groups:
31
+ python-minor:
32
+ update-types: [minor, patch]
33
+
34
+ - package-ecosystem: docker
35
+ directory: /deploy
36
+ schedule:
37
+ interval: weekly
38
+ day: monday
39
+ cooldown:
40
+ default-days: 7
41
+ open-pull-requests-limit: 5
42
+ commit-message:
43
+ prefix: build(docker)
@@ -0,0 +1,552 @@
1
+ name: ci
2
+
3
+ # Every change runs the same checks a release depends on: lint, types, tests on three systems, the docs, the Python
4
+ # package, both container images and the installers. The job names below are the required checks of the main branch;
5
+ # rename one only together with the branch protection rule.
6
+
7
+ on:
8
+ push:
9
+ branches: [main]
10
+ pull_request:
11
+ workflow_dispatch:
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ concurrency:
17
+ group: ci-${{ github.ref }}
18
+ cancel-in-progress: true
19
+
20
+ jobs:
21
+ lint:
22
+ name: Lint
23
+ runs-on: ubuntu-latest
24
+ timeout-minutes: 15
25
+ permissions:
26
+ contents: read
27
+ # zizmor uploads its findings to the Security tab.
28
+ security-events: write
29
+ steps:
30
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
31
+ with:
32
+ persist-credentials: false
33
+
34
+ - uses: ./.github/actions/setup # zizmor: ignore[self-repository] actionlint 1.7.12 rejects the $/ syntax
35
+
36
+ - name: Lint
37
+ run: ruff check .
38
+
39
+ - name: Check formatting
40
+ run: ruff format --check .
41
+
42
+ # Checks the syntax, the expressions and the shell scripts of every workflow.
43
+ - name: Check the workflows with actionlint
44
+ uses: docker://rhysd/actionlint:1.7.12@sha256:b1934ee5f1c509618f2508e6eb47ee0d3520686341fec936f3b79331f9315667
45
+ with:
46
+ args: -color
47
+
48
+ # Looks for security problems in the workflows (unpinned actions, injectable expressions, wide permissions).
49
+ # First as a gate: the findings are printed in the log and fail the job.
50
+ - name: Check the workflows with zizmor
51
+ uses: zizmorcore/zizmor-action@85b2b9fb8e3325e7a23d7d0a9d7ba5ae0f999e3e # v0.7.1
52
+ with:
53
+ advanced-security: false
54
+
55
+ # Then again as a report for the Security tab. A pull request from a fork has a read-only token and cannot
56
+ # upload there, so the report is skipped for it.
57
+ - name: Report the zizmor findings to the Security tab
58
+ if: ${{ !cancelled() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) }}
59
+ uses: zizmorcore/zizmor-action@85b2b9fb8e3325e7a23d7d0a9d7ba5ae0f999e3e # v0.7.1
60
+ with:
61
+ advanced-security: true
62
+
63
+ typecheck:
64
+ name: Type check
65
+ runs-on: ubuntu-latest
66
+ timeout-minutes: 15
67
+ steps:
68
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
69
+ with:
70
+ persist-credentials: false
71
+
72
+ - uses: ./.github/actions/setup # zizmor: ignore[self-repository] actionlint 1.7.12 rejects the $/ syntax
73
+
74
+ # The configuration lives in pyproject.toml ([tool.mypy]).
75
+ - name: mypy
76
+ run: python -m mypy
77
+
78
+ test:
79
+ name: Tests (${{ matrix.os }}, py${{ matrix.python }})
80
+ runs-on: ${{ matrix.os }}
81
+ timeout-minutes: 30
82
+ strategy:
83
+ fail-fast: false
84
+ matrix:
85
+ include:
86
+ # Only this leg measures coverage and keeps the report.
87
+ - { os: ubuntu-latest, python: "3.12", coverage: true }
88
+ - { os: ubuntu-latest, python: "3.13" }
89
+ - { os: macos-14, python: "3.12" }
90
+ - { os: windows-2022, python: "3.12" }
91
+ steps:
92
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
93
+ with:
94
+ persist-credentials: false
95
+
96
+ - uses: ./.github/actions/setup # zizmor: ignore[self-repository] actionlint 1.7.12 rejects the $/ syntax
97
+ with:
98
+ python-version: ${{ matrix.python }}
99
+
100
+ - name: Test
101
+ if: ${{ !matrix.coverage }}
102
+ run: pytest
103
+
104
+ - name: Test with coverage
105
+ if: ${{ matrix.coverage }}
106
+ run: pytest --cov=vpcatlas --cov-report=xml --cov-report=term --cov-fail-under=88
107
+
108
+ # The total and the five files with the lowest line coverage, in the summary of the run.
109
+ - name: Coverage summary
110
+ if: ${{ always() && matrix.coverage }}
111
+ run: |
112
+ python - <<'PY'
113
+ import os
114
+ import xml.etree.ElementTree as ET
115
+
116
+ if not os.path.exists("coverage.xml"):
117
+ raise SystemExit("coverage.xml was not written")
118
+ root = ET.parse("coverage.xml").getroot()
119
+ rows = []
120
+ for cls in root.iter("class"):
121
+ lines = cls.findall("./lines/line")
122
+ if not lines:
123
+ continue
124
+ hit = sum(1 for line in lines if int(line.get("hits", "0")) > 0)
125
+ rows.append((hit / len(lines), len(lines) - hit, cls.get("filename", "?")))
126
+ rows.sort(key=lambda row: (row[0], -row[1], row[2]))
127
+ total = float(root.get("line-rate", "0")) * 100
128
+ out = ["## Coverage", "", f"Total line coverage: **{total:.1f}%** (the gate is 88%)", ""]
129
+ out += ["Five least covered files:", "", "| File | Covered | Lines missed |", "|---|---:|---:|"]
130
+ out += [f"| `{name}` | {rate * 100:.1f}% | {missed} |" for rate, missed, name in rows[:5]]
131
+ with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as summary:
132
+ summary.write("\n".join(out) + "\n")
133
+ PY
134
+
135
+ - name: Keep the coverage report
136
+ if: ${{ always() && matrix.coverage }}
137
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
138
+ with:
139
+ name: coverage-xml
140
+ path: coverage.xml
141
+ if-no-files-found: ignore
142
+
143
+ docs:
144
+ name: Docs
145
+ runs-on: ubuntu-latest
146
+ timeout-minutes: 15
147
+ steps:
148
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
149
+ with:
150
+ persist-credentials: false
151
+
152
+ - uses: ./.github/actions/setup # zizmor: ignore[self-repository] actionlint 1.7.12 rejects the $/ syntax
153
+ with:
154
+ extras: docs
155
+
156
+ # --strict turns every broken link and every warning into an error.
157
+ - name: Build the site
158
+ run: mkdocs build --strict
159
+
160
+ - name: Keep the site
161
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
162
+ with:
163
+ name: docs-site
164
+ path: site/
165
+ retention-days: 3
166
+ if-no-files-found: error
167
+
168
+ package:
169
+ name: Python package
170
+ runs-on: ubuntu-latest
171
+ timeout-minutes: 15
172
+ steps:
173
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
174
+ with:
175
+ persist-credentials: false
176
+
177
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
178
+ with:
179
+ python-version: "3.12"
180
+
181
+ # The same steps as the package job of the release workflow, so a broken package shows up before the tag.
182
+ - name: Build the sdist and the wheel
183
+ run: |
184
+ python -m pip install --require-hashes -r requirements-release.lock
185
+ python -m build --no-isolation
186
+ twine check dist/*
187
+
188
+ - name: The wheel carries the dashboard
189
+ run: |
190
+ wheels=(dist/*.whl)
191
+ wheel="${wheels[0]}"
192
+ for file in dashboard/templates/index.html dashboard/static/app.js auditor-policy.json; do
193
+ unzip -l "$wheel" | grep -q "vpcatlas/${file}" || { echo "${file} is missing from ${wheel}" >&2; exit 1; }
194
+ done
195
+
196
+ - uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
197
+ with:
198
+ name: python-dist
199
+ path: dist/
200
+ retention-days: 3
201
+ if-no-files-found: error
202
+
203
+ images:
204
+ name: Images
205
+ runs-on: ubuntu-latest
206
+ timeout-minutes: 40
207
+ permissions:
208
+ contents: read
209
+ # The Trivy report is uploaded to the Security tab.
210
+ security-events: write
211
+ steps:
212
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
213
+ with:
214
+ persist-credentials: false
215
+
216
+ - uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
217
+
218
+ - name: Build the dashboard image
219
+ uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
220
+ with:
221
+ context: .
222
+ file: deploy/Dockerfile
223
+ load: true
224
+ push: false
225
+ tags: vpcatlas:ci
226
+ cache-from: type=gha,scope=dashboard
227
+ cache-to: type=gha,scope=dashboard,mode=max
228
+
229
+ - name: Smoke test the dashboard image
230
+ run: |
231
+ docker run -d --name atlas -p 8080:8080 -e AWS_EC2_METADATA_DISABLED=true vpcatlas:ci
232
+ for _ in $(seq 1 30); do
233
+ if curl -fsS http://127.0.0.1:8080/healthz > /dev/null; then break; fi
234
+ sleep 2
235
+ done
236
+ curl -fsS http://127.0.0.1:8080/healthz
237
+ echo
238
+ curl -fsS http://127.0.0.1:8080/ | grep -q "VPC Atlas"
239
+ curl -fsS http://127.0.0.1:8080/static/app.js > /dev/null
240
+ docker exec atlas python -m vpcatlas.dashboard.healthcheck
241
+ docker exec atlas vpcatlas --help > /dev/null
242
+
243
+ - name: Container logs
244
+ if: always()
245
+ run: docker logs atlas || true
246
+
247
+ - name: Build the Lambda image
248
+ uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0
249
+ with:
250
+ context: .
251
+ file: deploy/Dockerfile.lambda
252
+ load: true
253
+ push: false
254
+ tags: vpcatlas-lambda:ci
255
+ cache-from: type=gha,scope=lambda
256
+ cache-to: type=gha,scope=lambda,mode=max
257
+
258
+ # The Lambda base image carries the Runtime Interface Emulator: it listens on port 8080 and runs the handler
259
+ # for every POST to /2015-03-31/functions/function/invocations. The event is what API Gateway (HTTP API v2)
260
+ # sends for GET /healthz.
261
+ - name: Smoke test the Lambda image
262
+ run: |
263
+ docker run -d --name lambda -p 9009:8080 vpcatlas-lambda:ci
264
+ event="${RUNNER_TEMP}/event.json"
265
+ out="${RUNNER_TEMP}/lambda-response.json"
266
+ cat > "$event" <<'JSON'
267
+ {"version":"2.0","routeKey":"GET /healthz","rawPath":"/healthz","rawQueryString":"","headers":{"host":"localhost","accept":"*/*"},"requestContext":{"accountId":"000000000000","apiId":"local","domainName":"localhost","http":{"method":"GET","path":"/healthz","protocol":"HTTP/1.1","sourceIp":"127.0.0.1","userAgent":"ci"},"requestId":"ci","routeKey":"GET /healthz","stage":"$default","time":"18/Oct/2026:00:00:00 +0000","timeEpoch":0},"isBase64Encoded":false}
268
+ JSON
269
+ for _ in $(seq 1 30); do
270
+ if curl -sS --max-time 30 -X POST "http://127.0.0.1:9009/2015-03-31/functions/function/invocations" -d "@${event}" -o "$out"; then break; fi
271
+ sleep 2
272
+ done
273
+ cat "$out"
274
+ echo
275
+ python3 -c 'import json, sys; sys.exit(0 if json.load(open(sys.argv[1])).get("statusCode") == 200 else 1)' "$out" ||
276
+ { echo "The Lambda handler did not answer with statusCode 200" >&2; exit 1; }
277
+
278
+ - name: Lambda container logs
279
+ if: failure()
280
+ run: docker logs lambda || true
281
+
282
+ # The report goes to the Security tab; this run never fails on it (the gate is the next-but-two step).
283
+ - name: Scan the dashboard image with Trivy
284
+ uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
285
+ with:
286
+ image-ref: vpcatlas:ci
287
+ format: sarif
288
+ output: trivy.sarif
289
+ severity: CRITICAL,HIGH
290
+ limit-severities-for-sarif: true
291
+ ignore-unfixed: true
292
+ exit-code: "0"
293
+
294
+ # A pull request from a fork has a read-only token and cannot upload.
295
+ - name: Upload the Trivy report
296
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
297
+ uses: github/codeql-action/upload-sarif@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # v4.39.0
298
+ with:
299
+ sarif_file: trivy.sarif
300
+ category: trivy-dashboard
301
+
302
+ - name: Image summary
303
+ if: always()
304
+ run: |
305
+ python3 - <<'PY'
306
+ import json
307
+ import os
308
+ import subprocess
309
+
310
+
311
+ def size_mb(image):
312
+ try:
313
+ raw = subprocess.run(
314
+ ["docker", "image", "inspect", "--format", "{{.Size}}", image],
315
+ check=True,
316
+ capture_output=True,
317
+ text=True,
318
+ ).stdout.strip()
319
+ return f"{int(raw) / 1_000_000:.1f} MB"
320
+ except (subprocess.CalledProcessError, ValueError):
321
+ return "not built"
322
+
323
+
324
+ counts = {"CRITICAL": 0, "HIGH": 0}
325
+ scanned = os.path.exists("trivy.sarif")
326
+ if scanned:
327
+ with open("trivy.sarif", encoding="utf-8") as handle:
328
+ report = json.load(handle)
329
+ for run in report.get("runs", []):
330
+ rules = {
331
+ rule.get("id"): rule.get("properties", {}).get("security-severity")
332
+ for rule in run.get("tool", {}).get("driver", {}).get("rules", [])
333
+ }
334
+ for result in run.get("results", []):
335
+ try:
336
+ score = float(rules.get(result.get("ruleId")) or 0)
337
+ except ValueError:
338
+ score = 0.0
339
+ counts["CRITICAL" if score >= 9.0 else "HIGH"] += 1
340
+ out = ["## Images", "", "| Image | Size |", "|---|---:|"]
341
+ out += [f"| dashboard (`vpcatlas:ci`) | {size_mb('vpcatlas:ci')} |"]
342
+ out += [f"| Lambda (`vpcatlas-lambda:ci`) | {size_mb('vpcatlas-lambda:ci')} |", ""]
343
+ if scanned:
344
+ out += ["Trivy, dashboard image, fixed vulnerabilities only:", ""]
345
+ out += ["| Severity | Findings |", "|---|---:|"]
346
+ out += [f"| CRITICAL | {counts['CRITICAL']} |", f"| HIGH | {counts['HIGH']} |"]
347
+ else:
348
+ out += ["Trivy report not written."]
349
+ with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as summary:
350
+ summary.write("\n".join(out) + "\n")
351
+ PY
352
+
353
+ # The gate: a critical vulnerability with a fix available fails the job.
354
+ - name: Gate on critical vulnerabilities
355
+ uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
356
+ with:
357
+ image-ref: vpcatlas:ci
358
+ format: table
359
+ severity: CRITICAL
360
+ ignore-unfixed: true
361
+ exit-code: "1"
362
+
363
+ - name: Gate on critical vulnerabilities (Lambda image)
364
+ uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
365
+ with:
366
+ image-ref: vpcatlas-lambda:ci
367
+ format: table
368
+ severity: CRITICAL
369
+ ignore-unfixed: true
370
+ exit-code: "1"
371
+
372
+ installer:
373
+ name: Installer on ${{ matrix.os }}
374
+ runs-on: ${{ matrix.os }}
375
+ timeout-minutes: 30
376
+ strategy:
377
+ fail-fast: false
378
+ matrix:
379
+ os: [windows-2022, ubuntu-22.04, macos-14]
380
+ steps:
381
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
382
+ with:
383
+ persist-credentials: false
384
+
385
+ - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
386
+ with:
387
+ python-version: "3.12"
388
+
389
+ # Builds the archive (a .zip on Windows, a .tar.gz elsewhere) and runs the program inside it (--version,
390
+ # serve --help, a real dashboard request).
391
+ - name: Build the archive
392
+ shell: bash
393
+ run: bash scripts/build-binary.sh
394
+
395
+ # Windows PowerShell 5.1, with the script run as text, the way `irm <url> | iex` runs it. The archive and its
396
+ # checksum come from dist/ instead of the network.
397
+ - name: Install through a pipe with Windows PowerShell
398
+ if: matrix.os == 'windows-2022'
399
+ shell: powershell
400
+ run: |
401
+ $ErrorActionPreference = "Stop"
402
+ $env:VPCATLAS_ARCHIVE = (Get-ChildItem dist/vpcatlas-*.zip).FullName
403
+ $env:VPCATLAS_SUMS = "$env:VPCATLAS_ARCHIVE.sha256"
404
+ Get-Content -Raw scripts/install.ps1 | Invoke-Expression
405
+ vpcatlas --version
406
+
407
+ - name: The shim and the persistent PATH work from a new PowerShell
408
+ if: matrix.os == 'windows-2022'
409
+ shell: pwsh
410
+ run: |
411
+ $p = [Environment]::GetEnvironmentVariable('Path', 'User')
412
+ $env:Path = "$p;$env:Path"
413
+ vpcatlas --version
414
+ vpcatlas serve --help | Out-Null
415
+
416
+ - name: The shim works from cmd.exe
417
+ if: matrix.os == 'windows-2022'
418
+ # The point of the step is cmd.exe itself: users run the shim from it.
419
+ shell: cmd # zizmor: ignore[misfeature]
420
+ run: |
421
+ set "PATH=%LOCALAPPDATA%\Programs\vpcatlas;%PATH%"
422
+ vpcatlas --version
423
+
424
+ - name: Install as a file with parameters into other directories
425
+ if: matrix.os == 'windows-2022'
426
+ shell: pwsh
427
+ run: |
428
+ $zip = (Get-ChildItem dist/vpcatlas-*.zip).FullName
429
+ ./scripts/install.ps1 -Archive $zip -Sums "$zip.sha256" -InstallDir "$env:RUNNER_TEMP\vpcatlas-bin" -DataDir "$env:RUNNER_TEMP\vpcatlas-home"
430
+ & "$env:RUNNER_TEMP\vpcatlas-bin\vpcatlas.cmd" --version
431
+
432
+ # Negative control: the same archive with a checksum file that does not match it. The installer has to stop with
433
+ # a checksum error and leave nothing in either directory.
434
+ - name: The installer refuses a wrong checksum
435
+ if: matrix.os == 'windows-2022'
436
+ shell: pwsh
437
+ run: |
438
+ $zip = (Get-ChildItem dist/vpcatlas-*.zip).FullName
439
+ $badSums = Join-Path $env:RUNNER_TEMP 'bad-SHA256SUMS'
440
+ $badBin = "$env:RUNNER_TEMP\bad-bin"
441
+ $badHome = "$env:RUNNER_TEMP\bad-home"
442
+ Set-Content -LiteralPath $badSums -Value (('0' * 64) + ' ' + [IO.Path]::GetFileName($zip))
443
+ $message = $null
444
+ try {
445
+ ./scripts/install.ps1 -Archive $zip -Sums $badSums -InstallDir $badBin -DataDir $badHome
446
+ } catch {
447
+ $message = $_.Exception.Message
448
+ }
449
+ if (-not $message) { throw 'The installer accepted a wrong checksum: no error was raised.' }
450
+ if ($message -notmatch 'checksum mismatch') { throw "The installer failed, but not on the checksum: $message" }
451
+ foreach ($dir in @($badBin, $badHome)) {
452
+ if (Test-Path -LiteralPath $dir) {
453
+ $left = @(Get-ChildItem -LiteralPath $dir -Recurse -Force | Where-Object { ($_.PSIsContainer -and $_.Name -match '^\d+\.\d+\.\d+') -or $_.Name -eq 'vpcatlas.cmd' })
454
+ if ($left.Count -gt 0) { throw "The installer left files behind after a wrong checksum: $($left.FullName -join ', ')" }
455
+ }
456
+ }
457
+ Write-Host "Refused as expected: $message"
458
+
459
+ - name: Keep the Windows archive
460
+ if: matrix.os == 'windows-2022'
461
+ uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
462
+ with:
463
+ name: windows-x86_64-zip
464
+ path: |
465
+ dist/vpcatlas-*.zip
466
+ dist/vpcatlas-*.zip.sha256
467
+ retention-days: 3
468
+ if-no-files-found: error
469
+
470
+ # The script is piped into sh, the way `curl -fsSL https://vpcatlas.com/install.sh | sh` runs it, with the
471
+ # default directories. The archive and its checksum come from dist/ instead of the network.
472
+ - name: Install through a pipe with sh
473
+ if: matrix.os != 'windows-2022'
474
+ shell: bash
475
+ run: |
476
+ archives=("$PWD"/dist/vpcatlas-*.tar.gz)
477
+ [ "${#archives[@]}" -eq 1 ] || { echo "expected one archive in dist/, found ${#archives[@]}" >&2; exit 1; }
478
+ export VPCATLAS_ARCHIVE="${archives[0]}"
479
+ export VPCATLAS_SUMS="${VPCATLAS_ARCHIVE}.sha256"
480
+ # The pipe is the point of this step, so the useless-cat warning does not apply.
481
+ # shellcheck disable=SC2002
482
+ cat scripts/install.sh | sh
483
+ # The script put the link in /usr/local/bin or in ~/.local/bin; a new shell would find it there.
484
+ export PATH="$HOME/.local/bin:/usr/local/bin:$PATH"
485
+ version="$(basename "$VPCATLAS_ARCHIVE" | sed -n 's/^vpcatlas-\(.*\)-[a-z]*-[a-z0-9_]*\.tar\.gz$/\1/p')"
486
+ [ -n "$version" ] || { echo "could not read the version from $VPCATLAS_ARCHIVE" >&2; exit 1; }
487
+ link="$(command -v vpcatlas)"
488
+ echo "link: ${link} -> $(readlink "$link")"
489
+ [ -L "$link" ] || { echo "${link} is not a symbolic link" >&2; exit 1; }
490
+ vpcatlas --version | grep -F "$version"
491
+ vpcatlas serve --help > /dev/null
492
+
493
+ - name: Install into other directories
494
+ if: matrix.os != 'windows-2022'
495
+ shell: bash
496
+ run: |
497
+ archives=("$PWD"/dist/vpcatlas-*.tar.gz)
498
+ export VPCATLAS_ARCHIVE="${archives[0]}"
499
+ export VPCATLAS_SUMS="${VPCATLAS_ARCHIVE}.sha256"
500
+ export VPCATLAS_INSTALL_DIR="${RUNNER_TEMP}/vpcatlas-bin"
501
+ export VPCATLAS_HOME="${RUNNER_TEMP}/vpcatlas-home"
502
+ sh scripts/install.sh
503
+ [ -L "${VPCATLAS_INSTALL_DIR}/vpcatlas" ] || { echo "no link in ${VPCATLAS_INSTALL_DIR}" >&2; exit 1; }
504
+ "${VPCATLAS_INSTALL_DIR}/vpcatlas" --version
505
+ [ -n "$(find "$VPCATLAS_HOME" -mindepth 2 -maxdepth 2 -name _internal)" ] ||
506
+ { echo "no installed folder in ${VPCATLAS_HOME}" >&2; exit 1; }
507
+
508
+ # Negative control: the same archive with a checksum file that does not match it. The installer has to stop
509
+ # with a checksum error and leave nothing in either directory.
510
+ - name: The installer refuses a wrong checksum
511
+ if: matrix.os != 'windows-2022'
512
+ shell: bash
513
+ run: |
514
+ archives=("$PWD"/dist/vpcatlas-*.tar.gz)
515
+ export VPCATLAS_ARCHIVE="${archives[0]}"
516
+ export VPCATLAS_SUMS="${RUNNER_TEMP}/bad-SHA256SUMS"
517
+ export VPCATLAS_INSTALL_DIR="${RUNNER_TEMP}/bad-bin"
518
+ export VPCATLAS_HOME="${RUNNER_TEMP}/bad-home"
519
+ printf '%s %s\n' "0000000000000000000000000000000000000000000000000000000000000000" "$(basename "$VPCATLAS_ARCHIVE")" > "$VPCATLAS_SUMS"
520
+ status=0
521
+ output="$(sh scripts/install.sh 2>&1)" || status=$?
522
+ echo "$output"
523
+ [ "$status" -ne 0 ] || { echo "The installer accepted a wrong checksum" >&2; exit 1; }
524
+ echo "$output" | grep -q "checksum mismatch" || { echo "The installer failed, but not on the checksum" >&2; exit 1; }
525
+ if [ -e "${VPCATLAS_INSTALL_DIR}/vpcatlas" ] || [ -L "${VPCATLAS_INSTALL_DIR}/vpcatlas" ]; then
526
+ echo "The installer left a link behind after a wrong checksum" >&2
527
+ exit 1
528
+ fi
529
+ if [ -d "$VPCATLAS_HOME" ] && [ -n "$(find "$VPCATLAS_HOME" -mindepth 1)" ]; then
530
+ echo "The installer left files behind after a wrong checksum" >&2
531
+ exit 1
532
+ fi
533
+ echo "Refused as expected"
534
+
535
+ dependency-review:
536
+ name: Dependency review
537
+ if: github.event_name == 'pull_request'
538
+ runs-on: ubuntu-latest
539
+ timeout-minutes: 10
540
+ permissions:
541
+ contents: read
542
+ # The summary is posted as a comment on the pull request.
543
+ pull-requests: write
544
+ steps:
545
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
546
+ with:
547
+ persist-credentials: false
548
+
549
+ - uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
550
+ with:
551
+ fail-on-severity: high
552
+ comment-summary-in-pr: on-failure