code-sandboxes 1.9.7__tar.gz → 1.9.9__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 (262) hide show
  1. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/CHANGELOG.md +26 -0
  2. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/PKG-INFO +1 -1
  3. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/__version__.py +1 -1
  4. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/attest.py +81 -21
  5. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_attest.py +76 -3
  6. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/.github/workflows/build.yml +0 -0
  7. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/.github/workflows/environments-live.yml +0 -0
  8. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/.github/workflows/py-code-style.yml +0 -0
  9. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/.github/workflows/py-tests.yml +0 -0
  10. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/.github/workflows/py-typing.yml +0 -0
  11. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/.github/workflows/reusable-python.yml +0 -0
  12. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/.gitignore +0 -0
  13. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/.licenserc.yaml +0 -0
  14. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/.pre-commit-config.yaml +0 -0
  15. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/.vscode/settings.json +0 -0
  16. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/Dockerfile +0 -0
  17. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/LICENSE +0 -0
  18. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/Makefile +0 -0
  19. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/README.md +0 -0
  20. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/build.log +0 -0
  21. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/__init__.py +0 -0
  22. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/base.py +0 -0
  23. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/bridge_mount.py +0 -0
  24. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/builds.py +0 -0
  25. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/cli.py +0 -0
  26. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/client.py +0 -0
  27. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/cloudflare_sandbox.py +0 -0
  28. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/commands.py +0 -0
  29. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/console.py +0 -0
  30. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/contents.py +0 -0
  31. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/coreweave_sandbox.py +0 -0
  32. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/datalayer_sandbox.py +0 -0
  33. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/daytona_sandbox.py +0 -0
  34. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/docker_sandbox.py +0 -0
  35. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/e2b_sandbox.py +0 -0
  36. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/__init__.py +0 -0
  37. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/__main__.py +0 -0
  38. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/accounts.py +0 -0
  39. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/adapters/__init__.py +0 -0
  40. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/adapters/datalayer.py +0 -0
  41. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/adapters/daytona.py +0 -0
  42. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/adapters/e2b.py +0 -0
  43. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/adapters/managed.py +0 -0
  44. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/adapters/modal.py +0 -0
  45. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/bases.py +0 -0
  46. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/breaker.py +0 -0
  47. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/build_secrets.py +0 -0
  48. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/builders.py +0 -0
  49. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/canonical.py +0 -0
  50. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/conformance.py +0 -0
  51. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/constraints/sandbox-contract-v1.txt +0 -0
  52. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/constraints/wheelhouse/README.md +0 -0
  53. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/constraints/wheelhouse/jupyter_server-2.21.0+datalayer.1-py3-none-any.whl +0 -0
  54. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/contract.py +0 -0
  55. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/doctor/__init__.py +0 -0
  56. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/doctor/build.py +0 -0
  57. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/doctor/datalayer_sandbox.py +0 -0
  58. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/doctor/report.schema.json +0 -0
  59. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/errors.py +0 -0
  60. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/files.py +0 -0
  61. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/image_import.py +0 -0
  62. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/lifecycle.py +0 -0
  63. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/policy.py +0 -0
  64. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/redact.py +0 -0
  65. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/resolve.py +0 -0
  66. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/schema.py +0 -0
  67. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/environments/spec.py +0 -0
  68. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/eval_sandbox.py +0 -0
  69. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/exceptions.py +0 -0
  70. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/filesystem.py +0 -0
  71. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/google_colab.py +0 -0
  72. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/google_colab_sandbox.py +0 -0
  73. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/interfaces.py +0 -0
  74. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/jupyter_ingress.py +0 -0
  75. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/jupyter_server_sandbox.py +0 -0
  76. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/kaggle.py +0 -0
  77. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/kaggle_execute.py +0 -0
  78. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/kaggle_live.py +0 -0
  79. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/kaggle_sandbox.py +0 -0
  80. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/lifecycle.py +0 -0
  81. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/manage.py +0 -0
  82. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/modal_sandbox.py +0 -0
  83. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/models.py +0 -0
  84. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/monty_sandbox.py +0 -0
  85. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/provider_ingress.py +0 -0
  86. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/code_sandboxes/providers.py +0 -0
  87. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docker/Dockerfile +0 -0
  88. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/.gitignore +0 -0
  89. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/LICENSE +0 -0
  90. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/Makefile +0 -0
  91. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/README.md +0 -0
  92. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/babel.config.js +0 -0
  93. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/api-reference/_category_.yml +0 -0
  94. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/api-reference/index.mdx +0 -0
  95. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/cli/_category_.yml +0 -0
  96. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/cli/index.mdx +0 -0
  97. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/contribute/_category_.yml +0 -0
  98. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/contribute/index.mdx +0 -0
  99. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/environments/_category_.yml +0 -0
  100. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/environments/contract.mdx +0 -0
  101. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/environments/index.mdx +0 -0
  102. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/environments/specification.mdx +0 -0
  103. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/environments/spike-2026-09.mdx +0 -0
  104. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/examples/_category_.yml +0 -0
  105. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/examples/index.mdx +0 -0
  106. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/guide/_category_.yml +0 -0
  107. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/guide/index.mdx +0 -0
  108. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/index.mdx +0 -0
  109. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/install/_category_.yml +0 -0
  110. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/install/index.mdx +0 -0
  111. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/provider-ingress.mdx +0 -0
  112. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/providers/_category_.yml +0 -0
  113. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/providers/cloudflare.mdx +0 -0
  114. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/providers/coreweave.mdx +0 -0
  115. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/providers/datalayer.mdx +0 -0
  116. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/providers/daytona.mdx +0 -0
  117. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/providers/docker.mdx +0 -0
  118. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/providers/e2b.mdx +0 -0
  119. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/providers/eval.mdx +0 -0
  120. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/providers/google-colab.mdx +0 -0
  121. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/providers/index.mdx +0 -0
  122. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/providers/jupyter-server.mdx +0 -0
  123. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/providers/kaggle.mdx +0 -0
  124. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/providers/modal.mdx +0 -0
  125. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docs/providers/monty.mdx +0 -0
  126. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/docusaurus.config.js +0 -0
  127. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/package.json +0 -0
  128. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/sidebars.js +0 -0
  129. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/src/components/HomepageRow1.js +0 -0
  130. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/src/components/HomepageRow1.module.css +0 -0
  131. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/src/components/HomepageRow2.js +0 -0
  132. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/src/components/HomepageRow2.module.css +0 -0
  133. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/src/components/HomepageRow3.js +0 -0
  134. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/src/components/HomepageRow3.module.css +0 -0
  135. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/src/css/custom.css +0 -0
  136. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/src/pages/index.module.css +0 -0
  137. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/src/pages/markdown-page.md +0 -0
  138. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/src/theme/CustomDocItem.tsx +0 -0
  139. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/src/theme/IconsTitle.tsx +0 -0
  140. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/bricks.svg +0 -0
  141. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/building-construction.svg +0 -0
  142. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/cloud-sun.svg +0 -0
  143. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/cloud.svg +0 -0
  144. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/collaboration.svg +0 -0
  145. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/copy-left.svg +0 -0
  146. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/datalayer/logo.png +0 -0
  147. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/datalayer/logo.svg +0 -0
  148. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/datalayer/personas/business.svg +0 -0
  149. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/datalayer/personas/data-scientist.svg +0 -0
  150. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/datalayer/personas/devops.svg +0 -0
  151. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/datalayer/project/project-line.svg +0 -0
  152. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/favicon.ico +0 -0
  153. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/jupyter.svg +0 -0
  154. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/open-source.svg +0 -0
  155. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/react-js.svg +0 -0
  156. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/react-js2.svg +0 -0
  157. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/ringed-planet.svg +0 -0
  158. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/robot.svg +0 -0
  159. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/rocket.svg +0 -0
  160. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/shield.svg +0 -0
  161. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/sparkles.svg +0 -0
  162. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/streamlit.gif +0 -0
  163. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/super-hero.svg +0 -0
  164. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/target.svg +0 -0
  165. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/docs/static/img/thumbs-up.svg +0 -0
  166. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/README.md +0 -0
  167. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/exec/Makefile +0 -0
  168. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/exec/cloudflare_sandbox_example.py +0 -0
  169. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/exec/coreweave_sandbox_example.py +0 -0
  170. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/exec/datalayer_sandbox_example.py +0 -0
  171. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/exec/daytona_sandbox_example.py +0 -0
  172. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/exec/docker_sandbox_example.py +0 -0
  173. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/exec/e2b_sandbox_example.py +0 -0
  174. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/exec/eval_sandbox_example.py +0 -0
  175. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/exec/google_colab_sandbox_example.py +0 -0
  176. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/exec/jupyter_server_sandbox_example.py +0 -0
  177. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/exec/kaggle_sandbox_example.py +0 -0
  178. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/exec/modal_sandbox_example.py +0 -0
  179. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/exec/monty_sandbox_example.py +0 -0
  180. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/repl/Makefile +0 -0
  181. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/repl/cloudflare_sandbox_example.py +0 -0
  182. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/repl/coreweave_sandbox_example.py +0 -0
  183. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/repl/datalayer_sandbox_example.py +0 -0
  184. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/repl/daytona_sandbox_example.py +0 -0
  185. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/repl/docker_sandbox_example.py +0 -0
  186. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/repl/e2b_sandbox_example.py +0 -0
  187. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/repl/eval_sandbox_example.py +0 -0
  188. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/repl/google_colab_sandbox_example.py +0 -0
  189. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/repl/jupyter_server_sandbox_example.py +0 -0
  190. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/repl/kaggle_sandbox_example.py +0 -0
  191. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/repl/modal_sandbox_example.py +0 -0
  192. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/examples/repl/monty_sandbox_example.py +0 -0
  193. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/pyproject.toml +0 -0
  194. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/schemas/environment-v1alpha1.json +0 -0
  195. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/__init__.py +0 -0
  196. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/conftest.py +0 -0
  197. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_a_datalayer_sandbox_can_be_interrupted.py +0 -0
  198. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_bridge_mount.py +0 -0
  199. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_builds.py +0 -0
  200. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_cli_exec.py +0 -0
  201. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_cli_repl.py +0 -0
  202. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_client.py +0 -0
  203. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_client_contents.py +0 -0
  204. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_client_files.py +0 -0
  205. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_client_outcome_error_output.py +0 -0
  206. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_cloudflare.py +0 -0
  207. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_commands_envelope.py +0 -0
  208. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_console.py +0 -0
  209. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_contents_contract.py +0 -0
  210. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_contents_live_matrix.py +0 -0
  211. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_coreweave.py +0 -0
  212. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_datalayer_sandbox.py +0 -0
  213. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_daytona.py +0 -0
  214. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_e2b.py +0 -0
  215. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_accounts.py +0 -0
  216. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_bases.py +0 -0
  217. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_breaker.py +0 -0
  218. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_build_secrets.py +0 -0
  219. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_builders.py +0 -0
  220. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_canonical.py +0 -0
  221. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_conformance.py +0 -0
  222. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_constraints.py +0 -0
  223. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_contract.py +0 -0
  224. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_cross_variant_report.py +0 -0
  225. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_datalayer_builder.py +0 -0
  226. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_daytona_builder.py +0 -0
  227. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_doctor.py +0 -0
  228. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_e2b_builder.py +0 -0
  229. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_errors.py +0 -0
  230. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_image_import.py +0 -0
  231. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_lifecycle.py +0 -0
  232. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_live_matrix.py +0 -0
  233. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_managed_builders.py +0 -0
  234. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_modal_builder.py +0 -0
  235. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_redact.py +0 -0
  236. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_resolve.py +0 -0
  237. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_environment_spec.py +0 -0
  238. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_eval.py +0 -0
  239. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_every_variant_has_a_real_interrupt.py +0 -0
  240. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_factory.py +0 -0
  241. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_google_colab.py +0 -0
  242. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_integration.py +0 -0
  243. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_jupyter_ingress.py +0 -0
  244. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_jupyter_server.py +0 -0
  245. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_jupyter_server_streaming.py +0 -0
  246. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_kaggle.py +0 -0
  247. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_kaggle_execute.py +0 -0
  248. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_kaggle_live.py +0 -0
  249. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_kaggle_session.py +0 -0
  250. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_kernel_client_compatibility.py +0 -0
  251. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_lifecycle_vocabulary.py +0 -0
  252. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_manage.py +0 -0
  253. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_manage_datalayer.py +0 -0
  254. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_modal_google_colab_sandbox.py +0 -0
  255. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_modal_session.py +0 -0
  256. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_models.py +0 -0
  257. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_output_line_fidelity.py +0 -0
  258. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_provider_catalog_names.py +0 -0
  259. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_provider_ingress.py +0 -0
  260. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_providers.py +0 -0
  261. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_sandboxes.py +0 -0
  262. {code_sandboxes-1.9.7 → code_sandboxes-1.9.9}/tests/test_the_kill_relaunch_drill.py +0 -0
@@ -8,6 +8,32 @@
8
8
 
9
9
  ## Unreleased
10
10
 
11
+ ## 1.9.9
12
+
13
+ - **cosign gets the same registry credential the build pushed with**
14
+ (`environments.attest`; PLAN_ENV.md D-17, E1-09). cosign has no AWS
15
+ credential chain of its own for ECR, unlike the boto3 client the scan is
16
+ read with. Found live on r1, 2026-09-14: `cosign sign` reached the
17
+ repository anonymously and was refused, plain `401 Unauthorized`, on
18
+ every real artifact this pipeline ever tried to sign. `Attestor` takes a
19
+ `registry_auth` — the same `{"DOCKER_CONFIG": <dir>}` shape the resolver
20
+ and the builder already read off a `BuildCredential` — and sets it on
21
+ cosign's own subprocess, added to this process's own environment rather
22
+ than replacing it.
23
+
24
+ ## 1.9.8
25
+
26
+ - **Every page of a scan's findings is read before deciding**
27
+ (`environments.attest`; PLAN_ENV.md E1-08, D-11). `describe_image_scan_
28
+ findings` paginates, and the attestor called it exactly once. Found live
29
+ on r1, 2026-09-14: the first real image with more findings than one page
30
+ had 1,547 enhanced findings, 31 of them critical, and the single-page read
31
+ saw a small enough slice that the decision passed — an image with real,
32
+ unreviewed critical vulnerabilities would have been signed and started. A
33
+ hard cap of 50 pages keeps a pathological registry from paginating
34
+ forever; hitting it logs that more findings went unread rather than
35
+ pretending the scan was complete.
36
+
11
37
  ## 1.9.7
12
38
 
13
39
  - **cosign signs again under `--use-signing-config=false`** (`environments
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: code_sandboxes
3
- Version: 1.9.7
3
+ Version: 1.9.9
4
4
  Project-URL: Home, https://github.com/datalayer/code-sandboxes
5
5
  Author-email: Datalayer <info@datalayer.io>
6
6
  License: BSD 3-Clause License
@@ -3,4 +3,4 @@
3
3
 
4
4
  """Code Sandboxes."""
5
5
 
6
- __version__ = "1.9.7"
6
+ __version__ = "1.9.9"
@@ -150,6 +150,15 @@ class Attestor:
150
150
  policy
151
151
  What the findings have to say. The owner's default unless an
152
152
  organization tightened it (E3-06).
153
+ registry_auth
154
+ What cosign reads the repository with, to sign it (D-17): the same
155
+ `{"DOCKER_CONFIG": <dir>}` shape the resolver and the builder already
156
+ take off a `BuildCredential`, the one attribute name every caller
157
+ reads regardless of what kind of credential it holds. cosign has no
158
+ AWS credential chain of its own for ECR, unlike the boto3 client the
159
+ scan is read with — found live, 2026-09-14: with none, `cosign sign`
160
+ reached the registry anonymously and was refused, `401 Unauthorized`,
161
+ on every real artifact this pipeline ever tried to sign.
153
162
  """
154
163
 
155
164
  def __init__(
@@ -166,6 +175,7 @@ class Attestor:
166
175
  scan_timeout_seconds: float = DEFAULT_SCAN_TIMEOUT_SECONDS,
167
176
  scan_interval_seconds: float = DEFAULT_SCAN_INTERVAL_SECONDS,
168
177
  log: Callable[[str], None] | None = None,
178
+ registry_auth: Mapping[str, str] | None = None,
169
179
  ) -> None:
170
180
  self._ecr = ecr
171
181
  self._cosign = (shutil.which("cosign") or "") if cosign is None else cosign
@@ -178,6 +188,7 @@ class Attestor:
178
188
  self._timeout = scan_timeout_seconds
179
189
  self._interval = scan_interval_seconds
180
190
  self._log = log or (lambda _line: None)
191
+ self._registry_auth = dict(registry_auth or {})
181
192
 
182
193
  # -- the scan -------------------------------------------------------------
183
194
 
@@ -289,32 +300,70 @@ class Attestor:
289
300
  detail={"repository": repository, "digest": digest},
290
301
  )
291
302
 
303
+ #: A hard cap on pages read, so a registry that never stops paginating
304
+ #: cannot wedge a build forever — this many pages is already far past any
305
+ #: real image's own finding count (E1-08, found live 2026-09-14: see
306
+ #: `_describe`'s own docstring for why a page limit is not optional).
307
+ _MAX_FINDING_PAGES = 50
308
+
292
309
  def _describe(self, *, repository: str, digest: str) -> tuple[str, dict[str, Any]]:
293
- """One `DescribeImageScanFindings`, as a status and what it answered."""
294
- try:
295
- answer = self._client().describe_image_scan_findings(
296
- repositoryName=repository, imageId={"imageDigest": digest}
297
- )
298
- except Exception as error:
299
- if _is_in_progress(error):
300
- return "IN_PROGRESS", {}
301
- if _is_missing(error):
310
+ """Every `DescribeImageScanFindings` page, as one status and one answer.
311
+
312
+ ECR paginates enhanced findings — `nextToken`, not a field this call
313
+ can ask to skip — and a single unpaginated call answers only its first
314
+ page. Found live, 2026-09-14: the first real artifact this pipeline
315
+ scanned had 1,547 enhanced findings across many pages and 31 of them
316
+ critical, and a single-page read passed a decision that should have
317
+ blocked, because none of those 31 were in the one page it happened to
318
+ see. Every page is read and merged before `decide` ever runs.
319
+ """
320
+ pages = 0
321
+ status = ""
322
+ enhanced: list[Any] = []
323
+ basic: list[Any] = []
324
+ completed_at: Any = None
325
+ token: str | None = None
326
+ while True:
327
+ try:
328
+ kwargs: dict[str, Any] = {
329
+ "repositoryName": repository,
330
+ "imageId": {"imageDigest": digest},
331
+ }
332
+ if token:
333
+ kwargs["nextToken"] = token
334
+ answer = self._client().describe_image_scan_findings(**kwargs)
335
+ except Exception as error:
336
+ if _is_in_progress(error):
337
+ return "IN_PROGRESS", {}
338
+ if _is_missing(error):
339
+ raise EnvironmentsError(
340
+ PROVIDER_ERROR,
341
+ f"The registry has no scan for {digest}: {error}",
342
+ detail={"repository": repository, "digest": digest},
343
+ ) from error
302
344
  raise EnvironmentsError(
303
345
  PROVIDER_ERROR,
304
- f"The registry has no scan for {digest}: {error}",
346
+ f"The scan of {digest} could not be read: {error}",
305
347
  detail={"repository": repository, "digest": digest},
306
348
  ) from error
307
- raise EnvironmentsError(
308
- PROVIDER_ERROR,
309
- f"The scan of {digest} could not be read: {error}",
310
- detail={"repository": repository, "digest": digest},
311
- ) from error
312
- status = str(((answer.get("imageScanStatus") or {}).get("status")) or "")
313
- findings = answer.get("imageScanFindings") or {}
349
+ status = str(((answer.get("imageScanStatus") or {}).get("status")) or "")
350
+ findings = answer.get("imageScanFindings") or {}
351
+ enhanced.extend(findings.get("enhancedFindings") or [])
352
+ basic.extend(findings.get("findings") or [])
353
+ completed_at = findings.get("imageScanCompletedAt") or completed_at
354
+ pages += 1
355
+ token = answer.get("nextToken")
356
+ if not token or pages >= self._MAX_FINDING_PAGES:
357
+ if token:
358
+ self._log(
359
+ f"Stopped reading {digest}'s scan after {pages} pages, "
360
+ "with more findings still unread"
361
+ )
362
+ break
314
363
  return status, {
315
- "findings": findings,
316
- "completed_at": findings.get("imageScanCompletedAt"),
317
- "scanner": "ECR enhanced" if findings.get("enhancedFindings") else "ECR basic",
364
+ "findings": {"enhancedFindings": enhanced, "findings": basic},
365
+ "completed_at": completed_at,
366
+ "scanner": "ECR enhanced" if enhanced else "ECR basic",
318
367
  }
319
368
 
320
369
  @staticmethod
@@ -421,9 +470,19 @@ class Attestor:
421
470
  return bool(answer.get("imageDetails"))
422
471
 
423
472
  def _invoke(self, command: Sequence[str]) -> subprocess.CompletedProcess[str]:
473
+ # `registry_auth` — same shape as the resolver's own — puts cosign's
474
+ # `DOCKER_CONFIG` beside the rest of this process's environment,
475
+ # never replacing it: `--key` alone still needs AWS's own chain to
476
+ # reach the KMS key, which this does not touch.
477
+ env = {**os.environ, **self._registry_auth} if self._registry_auth else None
424
478
  try:
425
479
  finished = self._run(
426
- list(command), capture_output=True, text=True, check=False, timeout=self._timeout
480
+ list(command),
481
+ capture_output=True,
482
+ text=True,
483
+ check=False,
484
+ timeout=self._timeout,
485
+ env=env,
427
486
  )
428
487
  except subprocess.TimeoutExpired as expired:
429
488
  raise EnvironmentsError(
@@ -514,6 +573,7 @@ def attest_artifact(
514
573
  key=str(getattr(credential, "signing_key", "") or ""),
515
574
  policy=policy,
516
575
  log=log,
576
+ registry_auth=getattr(credential, "registry_auth", None),
517
577
  )
518
578
  return use.attest(
519
579
  registry=registry,
@@ -90,9 +90,14 @@ class FakeEcr:
90
90
  enhanced_findings=True,
91
91
  signed: set[str] | None = None,
92
92
  manifest: dict | None = None,
93
+ pages: list[list[dict]] | None = None,
93
94
  ) -> None:
94
95
  self.statuses = list(statuses)
95
96
  self.findings = list(findings)
97
+ #: Each entry is one `describe_image_scan_findings` page's own
98
+ #: findings, read in order and merged by `_describe` — overrides
99
+ #: `findings` when given, to test reading more than one page.
100
+ self.pages = pages
96
101
  self.enhanced_findings = enhanced_findings
97
102
  self.signed = set(signed or set())
98
103
  self.manifest = manifest
@@ -115,7 +120,7 @@ class FakeEcr:
115
120
  ]
116
121
  }
117
122
 
118
- def describe_image_scan_findings(self, repositoryName, imageId): # noqa: N803 - boto3's spelling
123
+ def describe_image_scan_findings(self, repositoryName, imageId, nextToken=None): # noqa: N803 - boto3's spelling
119
124
  self.asked += 1
120
125
  self.scanned.append(imageId["imageDigest"])
121
126
  status = self.statuses[min(self.asked, len(self.statuses)) - 1]
@@ -132,6 +137,18 @@ class FakeEcr:
132
137
  error.response = {"Error": {"Code": "ImageNotFoundException"}}
133
138
  raise error
134
139
  key = "enhancedFindings" if self.enhanced_findings else "findings"
140
+ if self.pages is not None:
141
+ index = int(nextToken or "0")
142
+ answer = {
143
+ "imageScanStatus": {"status": status},
144
+ "imageScanFindings": {
145
+ key: list(self.pages[index]),
146
+ "imageScanCompletedAt": "2026-09-12T09:00:00Z",
147
+ },
148
+ }
149
+ if index + 1 < len(self.pages):
150
+ answer["nextToken"] = str(index + 1)
151
+ return answer
135
152
  return {
136
153
  "imageScanStatus": {"status": status},
137
154
  "imageScanFindings": {
@@ -151,14 +168,16 @@ class FakeEcr:
151
168
 
152
169
 
153
170
  class Cosign:
154
- """A cosign whose argv is read, and which can refuse."""
171
+ """A cosign whose argv and env are read, and which can refuse."""
155
172
 
156
173
  def __init__(self, returncode: int = 0) -> None:
157
174
  self.returncode = returncode
158
175
  self.argv: list[str] = []
176
+ self.env: dict[str, str] | None = None
159
177
 
160
- def __call__(self, argv, **_kwargs) -> subprocess.CompletedProcess[str]:
178
+ def __call__(self, argv, *, env=None, **_kwargs) -> subprocess.CompletedProcess[str]:
161
179
  self.argv = list(argv)
180
+ self.env = env
162
181
  return subprocess.CompletedProcess(self.argv, self.returncode, "", "Pushing signature\n")
163
182
 
164
183
 
@@ -327,6 +346,31 @@ class TestTheScan:
327
346
  assert raised.value.code.code == "DL_ENV_PROVIDER_ERROR"
328
347
  assert raised.value.detail["digest"] == DIGEST
329
348
 
349
+ def test_every_page_of_findings_is_read_before_deciding(self) -> None:
350
+ """Found live, 2026-09-14: a single unpaginated call answered only its
351
+ first page, and a real image with 1,547 findings across many pages
352
+ passed a decision that should have blocked — none of its 31 critical
353
+ findings were on the one page a single call happened to see. Every
354
+ page is now read and merged before `decide` runs."""
355
+ page_1 = [enhanced("CVE-2026-1111", "LOW")]
356
+ page_2 = [enhanced("CVE-2026-1234", "CRITICAL", fixed="2.9.15")]
357
+ page_3 = [enhanced("CVE-2026-2222", "MEDIUM")]
358
+ ecr = FakeEcr(pages=[page_1, page_2, page_3])
359
+ decision = an_attestor(ecr=ecr).scan(repository=REPOSITORY, digest=DIGEST)
360
+ assert decision.decision == "blocked"
361
+ assert [finding.id for finding in decision.blocking] == ["CVE-2026-1234"]
362
+ assert decision.body()["counts"] == {"LOW": 1, "CRITICAL": 1, "MEDIUM": 1}
363
+ assert ecr.asked == 3
364
+
365
+ def test_reading_pages_stops_at_the_cap_rather_than_paginating_forever(self) -> None:
366
+ pages = [
367
+ [enhanced(f"CVE-2026-{n:04d}", "LOW")] for n in range(Attestor._MAX_FINDING_PAGES + 5)
368
+ ]
369
+ ecr = FakeEcr(pages=pages)
370
+ decision = an_attestor(ecr=ecr).scan(repository=REPOSITORY, digest=DIGEST)
371
+ assert decision.decision == "pass"
372
+ assert ecr.asked == Attestor._MAX_FINDING_PAGES
373
+
330
374
  IMAGE = "sha256:" + "bb" * 32
331
375
  ATTESTATION = "sha256:" + "cc" * 32
332
376
 
@@ -429,6 +473,35 @@ class TestTheSignature:
429
473
  assert reference == f"{REGISTRY}/{REPOSITORY}:{signature_tag(DIGEST)}"
430
474
  assert signed_now is True
431
475
 
476
+ def test_no_registry_auth_leaves_cosigns_own_environment_untouched(self) -> None:
477
+ """The common case — nothing to add — inherits this process's own
478
+ environment rather than replacing it with an empty one."""
479
+ cosign = Cosign()
480
+ an_attestor(run=cosign).sign(registry=REGISTRY, repository=REPOSITORY, digest=DIGEST)
481
+ assert cosign.env is None
482
+
483
+ def test_registry_auth_reaches_cosign_as_docker_config(self) -> None:
484
+ """cosign has no AWS credential chain of its own for ECR, unlike the
485
+ boto3 client the scan is read with: found live, 2026-09-14, `cosign
486
+ sign` reached the registry anonymously and was refused with a plain
487
+ `401 Unauthorized` on every real artifact this pipeline tried to
488
+ sign. The same `{"DOCKER_CONFIG": <dir>}` the resolver and the
489
+ builder already take off a `BuildCredential` fixes it here too."""
490
+ docker_config = "/tmp/dl-docker-abc" # noqa: S108 - a build's own dir, never touched by this test
491
+ cosign = Cosign()
492
+ an_attestor(run=cosign, registry_auth={"DOCKER_CONFIG": docker_config}).sign(
493
+ registry=REGISTRY, repository=REPOSITORY, digest=DIGEST
494
+ )
495
+ assert cosign.env is not None
496
+ assert cosign.env["DOCKER_CONFIG"] == docker_config
497
+ # Added to this process's own environment, not instead of it — cosign
498
+ # still needs AWS's own chain to reach the KMS key with `--key`.
499
+ import os
500
+
501
+ for name in ("PATH", "HOME"):
502
+ if name in os.environ:
503
+ assert cosign.env[name] == os.environ[name]
504
+
432
505
  def test_a_replay_finds_the_signature_instead_of_pushing_a_second(self) -> None:
433
506
  """Immutable tags would refuse the second, and two signatures are two words."""
434
507
  ecr = FakeEcr(signed={signature_tag(DIGEST)})
File without changes
File without changes
File without changes
File without changes