code-sandboxes 1.9.7__tar.gz → 1.9.8__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.8}/CHANGELOG.md +13 -0
  2. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/PKG-INFO +1 -1
  3. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/__version__.py +1 -1
  4. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/attest.py +58 -20
  5. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_attest.py +43 -1
  6. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/.github/workflows/build.yml +0 -0
  7. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/.github/workflows/environments-live.yml +0 -0
  8. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/.github/workflows/py-code-style.yml +0 -0
  9. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/.github/workflows/py-tests.yml +0 -0
  10. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/.github/workflows/py-typing.yml +0 -0
  11. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/.github/workflows/reusable-python.yml +0 -0
  12. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/.gitignore +0 -0
  13. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/.licenserc.yaml +0 -0
  14. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/.pre-commit-config.yaml +0 -0
  15. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/.vscode/settings.json +0 -0
  16. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/Dockerfile +0 -0
  17. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/LICENSE +0 -0
  18. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/Makefile +0 -0
  19. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/README.md +0 -0
  20. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/build.log +0 -0
  21. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/__init__.py +0 -0
  22. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/base.py +0 -0
  23. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/bridge_mount.py +0 -0
  24. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/builds.py +0 -0
  25. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/cli.py +0 -0
  26. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/client.py +0 -0
  27. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/cloudflare_sandbox.py +0 -0
  28. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/commands.py +0 -0
  29. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/console.py +0 -0
  30. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/contents.py +0 -0
  31. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/coreweave_sandbox.py +0 -0
  32. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/datalayer_sandbox.py +0 -0
  33. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/daytona_sandbox.py +0 -0
  34. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/docker_sandbox.py +0 -0
  35. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/e2b_sandbox.py +0 -0
  36. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/__init__.py +0 -0
  37. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/__main__.py +0 -0
  38. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/accounts.py +0 -0
  39. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/adapters/__init__.py +0 -0
  40. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/adapters/datalayer.py +0 -0
  41. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/adapters/daytona.py +0 -0
  42. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/adapters/e2b.py +0 -0
  43. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/adapters/managed.py +0 -0
  44. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/adapters/modal.py +0 -0
  45. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/bases.py +0 -0
  46. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/breaker.py +0 -0
  47. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/build_secrets.py +0 -0
  48. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/builders.py +0 -0
  49. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/canonical.py +0 -0
  50. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/conformance.py +0 -0
  51. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/constraints/sandbox-contract-v1.txt +0 -0
  52. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/constraints/wheelhouse/README.md +0 -0
  53. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/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.8}/code_sandboxes/environments/contract.py +0 -0
  55. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/doctor/__init__.py +0 -0
  56. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/doctor/build.py +0 -0
  57. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/doctor/datalayer_sandbox.py +0 -0
  58. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/doctor/report.schema.json +0 -0
  59. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/errors.py +0 -0
  60. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/files.py +0 -0
  61. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/image_import.py +0 -0
  62. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/lifecycle.py +0 -0
  63. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/policy.py +0 -0
  64. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/redact.py +0 -0
  65. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/resolve.py +0 -0
  66. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/schema.py +0 -0
  67. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/environments/spec.py +0 -0
  68. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/eval_sandbox.py +0 -0
  69. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/exceptions.py +0 -0
  70. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/filesystem.py +0 -0
  71. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/google_colab.py +0 -0
  72. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/google_colab_sandbox.py +0 -0
  73. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/interfaces.py +0 -0
  74. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/jupyter_ingress.py +0 -0
  75. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/jupyter_server_sandbox.py +0 -0
  76. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/kaggle.py +0 -0
  77. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/kaggle_execute.py +0 -0
  78. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/kaggle_live.py +0 -0
  79. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/kaggle_sandbox.py +0 -0
  80. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/lifecycle.py +0 -0
  81. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/manage.py +0 -0
  82. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/modal_sandbox.py +0 -0
  83. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/models.py +0 -0
  84. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/monty_sandbox.py +0 -0
  85. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/provider_ingress.py +0 -0
  86. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/code_sandboxes/providers.py +0 -0
  87. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docker/Dockerfile +0 -0
  88. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/.gitignore +0 -0
  89. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/LICENSE +0 -0
  90. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/Makefile +0 -0
  91. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/README.md +0 -0
  92. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/babel.config.js +0 -0
  93. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/api-reference/_category_.yml +0 -0
  94. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/api-reference/index.mdx +0 -0
  95. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/cli/_category_.yml +0 -0
  96. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/cli/index.mdx +0 -0
  97. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/contribute/_category_.yml +0 -0
  98. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/contribute/index.mdx +0 -0
  99. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/environments/_category_.yml +0 -0
  100. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/environments/contract.mdx +0 -0
  101. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/environments/index.mdx +0 -0
  102. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/environments/specification.mdx +0 -0
  103. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/environments/spike-2026-09.mdx +0 -0
  104. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/examples/_category_.yml +0 -0
  105. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/examples/index.mdx +0 -0
  106. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/guide/_category_.yml +0 -0
  107. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/guide/index.mdx +0 -0
  108. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/index.mdx +0 -0
  109. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/install/_category_.yml +0 -0
  110. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/install/index.mdx +0 -0
  111. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/provider-ingress.mdx +0 -0
  112. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/providers/_category_.yml +0 -0
  113. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/providers/cloudflare.mdx +0 -0
  114. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/providers/coreweave.mdx +0 -0
  115. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/providers/datalayer.mdx +0 -0
  116. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/providers/daytona.mdx +0 -0
  117. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/providers/docker.mdx +0 -0
  118. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/providers/e2b.mdx +0 -0
  119. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/providers/eval.mdx +0 -0
  120. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/providers/google-colab.mdx +0 -0
  121. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/providers/index.mdx +0 -0
  122. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/providers/jupyter-server.mdx +0 -0
  123. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/providers/kaggle.mdx +0 -0
  124. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/providers/modal.mdx +0 -0
  125. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docs/providers/monty.mdx +0 -0
  126. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/docusaurus.config.js +0 -0
  127. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/package.json +0 -0
  128. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/sidebars.js +0 -0
  129. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/src/components/HomepageRow1.js +0 -0
  130. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/src/components/HomepageRow1.module.css +0 -0
  131. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/src/components/HomepageRow2.js +0 -0
  132. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/src/components/HomepageRow2.module.css +0 -0
  133. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/src/components/HomepageRow3.js +0 -0
  134. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/src/components/HomepageRow3.module.css +0 -0
  135. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/src/css/custom.css +0 -0
  136. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/src/pages/index.module.css +0 -0
  137. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/src/pages/markdown-page.md +0 -0
  138. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/src/theme/CustomDocItem.tsx +0 -0
  139. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/src/theme/IconsTitle.tsx +0 -0
  140. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/bricks.svg +0 -0
  141. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/building-construction.svg +0 -0
  142. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/cloud-sun.svg +0 -0
  143. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/cloud.svg +0 -0
  144. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/collaboration.svg +0 -0
  145. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/copy-left.svg +0 -0
  146. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/datalayer/logo.png +0 -0
  147. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/datalayer/logo.svg +0 -0
  148. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/datalayer/personas/business.svg +0 -0
  149. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/datalayer/personas/data-scientist.svg +0 -0
  150. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/datalayer/personas/devops.svg +0 -0
  151. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/datalayer/project/project-line.svg +0 -0
  152. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/favicon.ico +0 -0
  153. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/jupyter.svg +0 -0
  154. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/open-source.svg +0 -0
  155. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/react-js.svg +0 -0
  156. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/react-js2.svg +0 -0
  157. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/ringed-planet.svg +0 -0
  158. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/robot.svg +0 -0
  159. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/rocket.svg +0 -0
  160. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/shield.svg +0 -0
  161. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/sparkles.svg +0 -0
  162. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/streamlit.gif +0 -0
  163. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/super-hero.svg +0 -0
  164. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/target.svg +0 -0
  165. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/docs/static/img/thumbs-up.svg +0 -0
  166. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/README.md +0 -0
  167. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/exec/Makefile +0 -0
  168. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/exec/cloudflare_sandbox_example.py +0 -0
  169. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/exec/coreweave_sandbox_example.py +0 -0
  170. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/exec/datalayer_sandbox_example.py +0 -0
  171. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/exec/daytona_sandbox_example.py +0 -0
  172. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/exec/docker_sandbox_example.py +0 -0
  173. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/exec/e2b_sandbox_example.py +0 -0
  174. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/exec/eval_sandbox_example.py +0 -0
  175. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/exec/google_colab_sandbox_example.py +0 -0
  176. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/exec/jupyter_server_sandbox_example.py +0 -0
  177. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/exec/kaggle_sandbox_example.py +0 -0
  178. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/exec/modal_sandbox_example.py +0 -0
  179. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/exec/monty_sandbox_example.py +0 -0
  180. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/repl/Makefile +0 -0
  181. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/repl/cloudflare_sandbox_example.py +0 -0
  182. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/repl/coreweave_sandbox_example.py +0 -0
  183. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/repl/datalayer_sandbox_example.py +0 -0
  184. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/repl/daytona_sandbox_example.py +0 -0
  185. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/repl/docker_sandbox_example.py +0 -0
  186. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/repl/e2b_sandbox_example.py +0 -0
  187. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/repl/eval_sandbox_example.py +0 -0
  188. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/repl/google_colab_sandbox_example.py +0 -0
  189. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/repl/jupyter_server_sandbox_example.py +0 -0
  190. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/repl/kaggle_sandbox_example.py +0 -0
  191. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/repl/modal_sandbox_example.py +0 -0
  192. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/examples/repl/monty_sandbox_example.py +0 -0
  193. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/pyproject.toml +0 -0
  194. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/schemas/environment-v1alpha1.json +0 -0
  195. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/__init__.py +0 -0
  196. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/conftest.py +0 -0
  197. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_a_datalayer_sandbox_can_be_interrupted.py +0 -0
  198. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_bridge_mount.py +0 -0
  199. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_builds.py +0 -0
  200. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_cli_exec.py +0 -0
  201. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_cli_repl.py +0 -0
  202. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_client.py +0 -0
  203. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_client_contents.py +0 -0
  204. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_client_files.py +0 -0
  205. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_client_outcome_error_output.py +0 -0
  206. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_cloudflare.py +0 -0
  207. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_commands_envelope.py +0 -0
  208. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_console.py +0 -0
  209. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_contents_contract.py +0 -0
  210. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_contents_live_matrix.py +0 -0
  211. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_coreweave.py +0 -0
  212. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_datalayer_sandbox.py +0 -0
  213. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_daytona.py +0 -0
  214. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_e2b.py +0 -0
  215. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_accounts.py +0 -0
  216. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_bases.py +0 -0
  217. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_breaker.py +0 -0
  218. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_build_secrets.py +0 -0
  219. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_builders.py +0 -0
  220. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_canonical.py +0 -0
  221. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_conformance.py +0 -0
  222. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_constraints.py +0 -0
  223. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_contract.py +0 -0
  224. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_cross_variant_report.py +0 -0
  225. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_datalayer_builder.py +0 -0
  226. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_daytona_builder.py +0 -0
  227. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_doctor.py +0 -0
  228. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_e2b_builder.py +0 -0
  229. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_errors.py +0 -0
  230. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_image_import.py +0 -0
  231. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_lifecycle.py +0 -0
  232. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_live_matrix.py +0 -0
  233. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_managed_builders.py +0 -0
  234. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_modal_builder.py +0 -0
  235. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_redact.py +0 -0
  236. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_resolve.py +0 -0
  237. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_environment_spec.py +0 -0
  238. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_eval.py +0 -0
  239. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_every_variant_has_a_real_interrupt.py +0 -0
  240. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_factory.py +0 -0
  241. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_google_colab.py +0 -0
  242. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_integration.py +0 -0
  243. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_jupyter_ingress.py +0 -0
  244. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_jupyter_server.py +0 -0
  245. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_jupyter_server_streaming.py +0 -0
  246. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_kaggle.py +0 -0
  247. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_kaggle_execute.py +0 -0
  248. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_kaggle_live.py +0 -0
  249. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_kaggle_session.py +0 -0
  250. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_kernel_client_compatibility.py +0 -0
  251. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_lifecycle_vocabulary.py +0 -0
  252. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_manage.py +0 -0
  253. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_manage_datalayer.py +0 -0
  254. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_modal_google_colab_sandbox.py +0 -0
  255. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_modal_session.py +0 -0
  256. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_models.py +0 -0
  257. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_output_line_fidelity.py +0 -0
  258. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_provider_catalog_names.py +0 -0
  259. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_provider_ingress.py +0 -0
  260. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_providers.py +0 -0
  261. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_sandboxes.py +0 -0
  262. {code_sandboxes-1.9.7 → code_sandboxes-1.9.8}/tests/test_the_kill_relaunch_drill.py +0 -0
@@ -8,6 +8,19 @@
8
8
 
9
9
  ## Unreleased
10
10
 
11
+ ## 1.9.8
12
+
13
+ - **Every page of a scan's findings is read before deciding**
14
+ (`environments.attest`; PLAN_ENV.md E1-08, D-11). `describe_image_scan_
15
+ findings` paginates, and the attestor called it exactly once. Found live
16
+ on r1, 2026-09-14: the first real image with more findings than one page
17
+ had 1,547 enhanced findings, 31 of them critical, and the single-page read
18
+ saw a small enough slice that the decision passed — an image with real,
19
+ unreviewed critical vulnerabilities would have been signed and started. A
20
+ hard cap of 50 pages keeps a pathological registry from paginating
21
+ forever; hitting it logs that more findings went unread rather than
22
+ pretending the scan was complete.
23
+
11
24
  ## 1.9.7
12
25
 
13
26
  - **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.8
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.8"
@@ -289,32 +289,70 @@ class Attestor:
289
289
  detail={"repository": repository, "digest": digest},
290
290
  )
291
291
 
292
+ #: A hard cap on pages read, so a registry that never stops paginating
293
+ #: cannot wedge a build forever — this many pages is already far past any
294
+ #: real image's own finding count (E1-08, found live 2026-09-14: see
295
+ #: `_describe`'s own docstring for why a page limit is not optional).
296
+ _MAX_FINDING_PAGES = 50
297
+
292
298
  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):
299
+ """Every `DescribeImageScanFindings` page, as one status and one answer.
300
+
301
+ ECR paginates enhanced findings — `nextToken`, not a field this call
302
+ can ask to skip — and a single unpaginated call answers only its first
303
+ page. Found live, 2026-09-14: the first real artifact this pipeline
304
+ scanned had 1,547 enhanced findings across many pages and 31 of them
305
+ critical, and a single-page read passed a decision that should have
306
+ blocked, because none of those 31 were in the one page it happened to
307
+ see. Every page is read and merged before `decide` ever runs.
308
+ """
309
+ pages = 0
310
+ status = ""
311
+ enhanced: list[Any] = []
312
+ basic: list[Any] = []
313
+ completed_at: Any = None
314
+ token: str | None = None
315
+ while True:
316
+ try:
317
+ kwargs: dict[str, Any] = {
318
+ "repositoryName": repository,
319
+ "imageId": {"imageDigest": digest},
320
+ }
321
+ if token:
322
+ kwargs["nextToken"] = token
323
+ answer = self._client().describe_image_scan_findings(**kwargs)
324
+ except Exception as error:
325
+ if _is_in_progress(error):
326
+ return "IN_PROGRESS", {}
327
+ if _is_missing(error):
328
+ raise EnvironmentsError(
329
+ PROVIDER_ERROR,
330
+ f"The registry has no scan for {digest}: {error}",
331
+ detail={"repository": repository, "digest": digest},
332
+ ) from error
302
333
  raise EnvironmentsError(
303
334
  PROVIDER_ERROR,
304
- f"The registry has no scan for {digest}: {error}",
335
+ f"The scan of {digest} could not be read: {error}",
305
336
  detail={"repository": repository, "digest": digest},
306
337
  ) 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 {}
338
+ status = str(((answer.get("imageScanStatus") or {}).get("status")) or "")
339
+ findings = answer.get("imageScanFindings") or {}
340
+ enhanced.extend(findings.get("enhancedFindings") or [])
341
+ basic.extend(findings.get("findings") or [])
342
+ completed_at = findings.get("imageScanCompletedAt") or completed_at
343
+ pages += 1
344
+ token = answer.get("nextToken")
345
+ if not token or pages >= self._MAX_FINDING_PAGES:
346
+ if token:
347
+ self._log(
348
+ f"Stopped reading {digest}'s scan after {pages} pages, "
349
+ "with more findings still unread"
350
+ )
351
+ break
314
352
  return status, {
315
- "findings": findings,
316
- "completed_at": findings.get("imageScanCompletedAt"),
317
- "scanner": "ECR enhanced" if findings.get("enhancedFindings") else "ECR basic",
353
+ "findings": {"enhancedFindings": enhanced, "findings": basic},
354
+ "completed_at": completed_at,
355
+ "scanner": "ECR enhanced" if enhanced else "ECR basic",
318
356
  }
319
357
 
320
358
  @staticmethod
@@ -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": {
@@ -327,6 +344,31 @@ class TestTheScan:
327
344
  assert raised.value.code.code == "DL_ENV_PROVIDER_ERROR"
328
345
  assert raised.value.detail["digest"] == DIGEST
329
346
 
347
+ def test_every_page_of_findings_is_read_before_deciding(self) -> None:
348
+ """Found live, 2026-09-14: a single unpaginated call answered only its
349
+ first page, and a real image with 1,547 findings across many pages
350
+ passed a decision that should have blocked — none of its 31 critical
351
+ findings were on the one page a single call happened to see. Every
352
+ page is now read and merged before `decide` runs."""
353
+ page_1 = [enhanced("CVE-2026-1111", "LOW")]
354
+ page_2 = [enhanced("CVE-2026-1234", "CRITICAL", fixed="2.9.15")]
355
+ page_3 = [enhanced("CVE-2026-2222", "MEDIUM")]
356
+ ecr = FakeEcr(pages=[page_1, page_2, page_3])
357
+ decision = an_attestor(ecr=ecr).scan(repository=REPOSITORY, digest=DIGEST)
358
+ assert decision.decision == "blocked"
359
+ assert [finding.id for finding in decision.blocking] == ["CVE-2026-1234"]
360
+ assert decision.body()["counts"] == {"LOW": 1, "CRITICAL": 1, "MEDIUM": 1}
361
+ assert ecr.asked == 3
362
+
363
+ def test_reading_pages_stops_at_the_cap_rather_than_paginating_forever(self) -> None:
364
+ pages = [
365
+ [enhanced(f"CVE-2026-{n:04d}", "LOW")] for n in range(Attestor._MAX_FINDING_PAGES + 5)
366
+ ]
367
+ ecr = FakeEcr(pages=pages)
368
+ decision = an_attestor(ecr=ecr).scan(repository=REPOSITORY, digest=DIGEST)
369
+ assert decision.decision == "pass"
370
+ assert ecr.asked == Attestor._MAX_FINDING_PAGES
371
+
330
372
  IMAGE = "sha256:" + "bb" * 32
331
373
  ATTESTATION = "sha256:" + "cc" * 32
332
374
 
File without changes
File without changes
File without changes
File without changes