@1agh/maude 1.0.11 → 1.3.0
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.
- package/README.md +3 -1
- package/apps/studio/acp/bridge.ts +18 -13
- package/apps/studio/acp/index.ts +43 -7
- package/apps/studio/acp/transcript-io.ts +394 -0
- package/apps/studio/acp/transcript.ts +103 -38
- package/apps/studio/annotations-layer.tsx +191 -116
- package/apps/studio/annotations-model.ts +39 -0
- package/apps/studio/annotations-sync.ts +50 -0
- package/apps/studio/api.ts +821 -55
- package/apps/studio/bin/_import-figma.mjs +57 -11
- package/apps/studio/bin/_seed-fixture.mjs +163 -0
- package/apps/studio/bin/annotate.mjs +3 -1
- package/apps/studio/bin/server-up.sh +17 -2
- package/apps/studio/canvas-edit.ts +391 -7
- package/apps/studio/canvas-imports.ts +89 -0
- package/apps/studio/canvas-lib.tsx +82 -18
- package/apps/studio/canvas-notice-message.ts +16 -0
- package/apps/studio/canvas-notifications.tsx +19 -0
- package/apps/studio/canvas-shell.tsx +57 -6
- package/apps/studio/client/app.jsx +943 -248
- package/apps/studio/client/apply-edit-request.ts +55 -0
- package/apps/studio/client/export-center.jsx +125 -67
- package/apps/studio/client/github.js +38 -1
- package/apps/studio/client/index-loader.ts +76 -0
- package/apps/studio/client/panels/CloudBar.jsx +52 -6
- package/apps/studio/client/panels/GitPanel.jsx +103 -0
- package/apps/studio/client/panels/OnboardingWizard.jsx +36 -7
- package/apps/studio/client/panels/RepoBranchSwitcher.jsx +92 -27
- package/apps/studio/client/panels/SourceConflictPanel.jsx +166 -0
- package/apps/studio/client/panels/SyncPanel.jsx +238 -4
- package/apps/studio/client/panels/TeamProjects.jsx +376 -0
- package/apps/studio/client/panels/file-deep-link-dialog.jsx +54 -0
- package/apps/studio/client/photo-knobs.jsx +3 -0
- package/apps/studio/client/report-bug.jsx +19 -1
- package/apps/studio/client/share-dialog.jsx +91 -0
- package/apps/studio/client/share-link.js +94 -0
- package/apps/studio/client/styles/3-shell-maude.css +69 -18
- package/apps/studio/client/styles/4-components.css +44 -1
- package/apps/studio/client/tree-row-menu.jsx +48 -3
- package/apps/studio/client/whats-new.jsx +26 -32
- package/apps/studio/cloud/endpoints.ts +132 -0
- package/apps/studio/collab/index.ts +4 -0
- package/apps/studio/collab/persistence.ts +137 -17
- package/apps/studio/collab/registry.ts +19 -21
- package/apps/studio/collab/room.ts +13 -2
- package/apps/studio/context.ts +59 -0
- package/apps/studio/debug-bundle.ts +13 -0
- package/apps/studio/dist/client.bundle.js +1911 -1879
- package/apps/studio/dist/runtime/.min-sizes.json +1 -0
- package/apps/studio/dist/runtime/sonner.js +1 -0
- package/apps/studio/dist/styles.css +1 -1
- package/apps/studio/export-dialog.tsx +54 -0
- package/apps/studio/exporters/degraded.ts +19 -3
- package/apps/studio/exporters/ghostscript.ts +194 -0
- package/apps/studio/exporters/pdf-fonts.ts +261 -0
- package/apps/studio/exporters/pdf.ts +152 -3
- package/apps/studio/exporters/remote.ts +36 -1
- package/apps/studio/fs-watch.ts +45 -1
- package/apps/studio/http.ts +296 -19
- package/apps/studio/inspect.ts +19 -0
- package/apps/studio/managed-projects.ts +150 -0
- package/apps/studio/mem.ts +45 -9
- package/apps/studio/notifications.tsx +292 -0
- package/apps/studio/runtime-bundle.ts +2 -0
- package/apps/studio/server.ts +69 -6
- package/apps/studio/sync/accepted-cold-start.ts +225 -0
- package/apps/studio/sync/accepted-link.ts +320 -0
- package/apps/studio/sync/action-stage.ts +343 -0
- package/apps/studio/sync/agent.ts +104 -45
- package/apps/studio/sync/asset-push.ts +1 -47
- package/apps/studio/sync/cell-file-events.ts +3 -0
- package/apps/studio/sync/codec.ts +192 -34
- package/apps/studio/sync/cold-start-apply.ts +7 -0
- package/apps/studio/sync/cold-start.ts +118 -22
- package/apps/studio/sync/comment-identity.ts +68 -0
- package/apps/studio/sync/connection-state.ts +15 -1
- package/apps/studio/sync/ctl-provider.ts +15 -2
- package/apps/studio/sync/document-discovery.ts +81 -0
- package/apps/studio/sync/file-ledger.ts +117 -0
- package/apps/studio/sync/file-membership.ts +10 -2
- package/apps/studio/sync/file-plane.ts +926 -47
- package/apps/studio/sync/index.ts +1786 -36
- package/apps/studio/sync/journal-client.ts +13 -1
- package/apps/studio/sync/migrate-seed.ts +150 -22
- package/apps/studio/sync/poke.ts +4 -2
- package/apps/studio/sync/presentation.ts +311 -5
- package/apps/studio/sync/projection.ts +820 -23
- package/apps/studio/sync/repeated-module.ts +73 -0
- package/apps/studio/sync/retry-after.ts +169 -0
- package/apps/studio/sync/revision-barrier.ts +129 -0
- package/apps/studio/sync/seed-progress.ts +218 -0
- package/apps/studio/sync/seed-repair.ts +46 -0
- package/apps/studio/sync/source-merge.ts +100 -0
- package/apps/studio/sync/source-ops.ts +289 -0
- package/apps/studio/sync/source-recovery.ts +70 -0
- package/apps/studio/sync/source-validation.ts +56 -0
- package/apps/studio/sync/status.ts +201 -6
- package/apps/studio/sync/transaction-client.ts +460 -0
- package/apps/studio/sync/transport-error.ts +111 -0
- package/apps/studio/sync/writer-registry.ts +236 -0
- package/apps/studio/text-caret.ts +35 -0
- package/apps/studio/undo-hud.tsx +9 -87
- package/apps/studio/use-canvas-media-drop.tsx +4 -39
- package/apps/studio/whats-new.json +173 -0
- package/cli/bin/maude.mjs +2 -0
- package/cli/commands/codex.mjs +191 -0
- package/cli/commands/harness.mjs +1051 -0
- package/cli/commands/help.mjs +13 -0
- package/cli/lib/design-link.mjs +190 -2
- package/cli/lib/harness/capabilities.mjs +302 -0
- package/cli/lib/harness/codex-runtime.mjs +892 -0
- package/cli/lib/harness/compatibility.mjs +27 -0
- package/cli/lib/harness/discover-claude.mjs +1393 -0
- package/cli/lib/harness/managed-state.mjs +1296 -0
- package/cli/lib/harness/model.mjs +86 -0
- package/cli/lib/harness/secrets.mjs +390 -0
- package/cli/lib/harness/targets/codex.mjs +1141 -0
- package/cli/lib/harness/targets/opencode.mjs +807 -0
- package/cli/lib/harness/transaction.mjs +1099 -0
- package/cli/lib/hubs-config.mjs +35 -8
- package/cli/templates/harness/opencode/maude-projector.ts +58 -0
- package/docs/harness-capability-matrix.md +125 -0
- package/docs/harness-environment-projection.md +95 -0
- package/package.json +33 -9
- package/plugins/design/dependencies.json +3 -3
- package/plugins/design/templates/_shell.html +47 -0
- package/plugins/flow/.claude-plugin/config.schema.json +8 -0
- package/plugins/flow/dependencies.json +3 -3
- package/apps/studio/.ai/cache/_stats.json +0 -7
- package/apps/studio/bin/_agent-browser-safe.test.mjs +0 -165
- package/apps/studio/bin/_curl-local.test.mjs +0 -280
- package/apps/studio/bin/_pw-launch.test.mjs +0 -90
- package/apps/studio/bin/_smart-frames.test.mjs +0 -195
- package/apps/studio/bin/_transcribe.test.mjs +0 -80
- package/apps/studio/bun.lock +0 -928
- package/apps/studio/draw/test/animate.test.ts +0 -172
- package/apps/studio/draw/test/brush.test.ts +0 -213
- package/apps/studio/draw/test/composition.test.ts +0 -141
- package/apps/studio/draw/test/geometry.test.ts +0 -199
- package/apps/studio/draw/test/gradient.test.ts +0 -167
- package/apps/studio/draw/test/layout.test.ts +0 -120
- package/apps/studio/draw/test/morph.test.ts +0 -107
- package/apps/studio/draw/test/optimize.test.ts +0 -40
- package/apps/studio/draw/test/palette.test.ts +0 -123
- package/apps/studio/draw/test/primitives.test.ts +0 -129
- package/apps/studio/draw/test/serialize-animate.test.ts +0 -142
- package/apps/studio/draw/test/serialize.test.ts +0 -105
- package/apps/studio/figma/assets.test.ts +0 -464
- package/apps/studio/figma/client.test.ts +0 -395
- package/apps/studio/figma/codegen-client.test.ts +0 -276
- package/apps/studio/figma/codegen-fonts.test.ts +0 -103
- package/apps/studio/figma/codegen-values.test.ts +0 -179
- package/apps/studio/figma/comments-to-strokes.test.ts +0 -194
- package/apps/studio/figma/fig-decode.test.ts +0 -788
- package/apps/studio/figma/fig-differential.test.ts +0 -182
- package/apps/studio/figma/fig-translator.test.ts +0 -192
- package/apps/studio/figma/fig-vector.test.ts +0 -113
- package/apps/studio/figma/from-codegen.test.ts +0 -408
- package/apps/studio/figma/sanitize.test.ts +0 -325
- package/apps/studio/figma/tailwind-map.test.ts +0 -142
- package/apps/studio/figma/to-artboard.test.ts +0 -808
- package/apps/studio/figma/to-render.test.ts +0 -180
- package/apps/studio/figma/to-strokes-roundtrip.test.ts +0 -152
- package/apps/studio/figma/to-strokes.test.ts +0 -705
- package/apps/studio/figma/to-tokens.test.ts +0 -321
- package/apps/studio/figma/url.test.ts +0 -167
- package/apps/studio/footage/schema.test.ts +0 -287
- package/apps/studio/generation/adapters/groq.test.ts +0 -80
- package/apps/studio/generation/audio-library.test.ts +0 -95
- package/apps/studio/generation/captions.test.ts +0 -78
- package/apps/studio/generation/elevenlabs.test.ts +0 -223
- package/apps/studio/generation/gemini.test.ts +0 -350
- package/apps/studio/generation/jobs.test.ts +0 -128
- package/apps/studio/generation/keys.test.ts +0 -75
- package/apps/studio/generation/prefs.test.ts +0 -95
- package/apps/studio/generation/types.test.ts +0 -123
- package/apps/studio/generation/whisper-models.test.ts +0 -179
- package/apps/studio/test/_helpers.ts +0 -108
- package/apps/studio/test/acp-activity-endpoint.test.ts +0 -183
- package/apps/studio/test/acp-activity.test.ts +0 -154
- package/apps/studio/test/acp-ai-activity.test.ts +0 -182
- package/apps/studio/test/acp-attachment-serve.test.ts +0 -74
- package/apps/studio/test/acp-bootstrap-brief.test.ts +0 -178
- package/apps/studio/test/acp-branch-guard.test.ts +0 -123
- package/apps/studio/test/acp-bridge-lifetime.test.ts +0 -475
- package/apps/studio/test/acp-bridge.test.ts +0 -545
- package/apps/studio/test/acp-capabilities.test.ts +0 -123
- package/apps/studio/test/acp-caps-bridge.test.ts +0 -279
- package/apps/studio/test/acp-commands.test.ts +0 -119
- package/apps/studio/test/acp-elicitation-bridge.test.ts +0 -480
- package/apps/studio/test/acp-elicitation.test.ts +0 -251
- package/apps/studio/test/acp-env.test.ts +0 -95
- package/apps/studio/test/acp-mode-banner.test.ts +0 -45
- package/apps/studio/test/acp-origin-gate.test.ts +0 -160
- package/apps/studio/test/acp-permission-prompt.test.ts +0 -251
- package/apps/studio/test/acp-permission.test.ts +0 -278
- package/apps/studio/test/acp-plugin-bootstrap.test.ts +0 -89
- package/apps/studio/test/acp-session-allowed-tools.test.ts +0 -235
- package/apps/studio/test/acp-session-plugins.test.ts +0 -209
- package/apps/studio/test/acp-toolgroup.test.ts +0 -76
- package/apps/studio/test/acp-transcript-view.test.ts +0 -71
- package/apps/studio/test/acp-transcript.test.ts +0 -238
- package/apps/studio/test/acp-usage-bridge.test.ts +0 -141
- package/apps/studio/test/acp-usage.test.ts +0 -143
- package/apps/studio/test/acp-write-gate.test.ts +0 -323
- package/apps/studio/test/acp-write-scope.test.ts +0 -533
- package/apps/studio/test/active-state.test.ts +0 -186
- package/apps/studio/test/activity.test.ts +0 -189
- package/apps/studio/test/ai-activity.test.ts +0 -113
- package/apps/studio/test/annotate-write.test.ts +0 -1074
- package/apps/studio/test/annotation-strokes-command.test.ts +0 -166
- package/apps/studio/test/annotations-align.test.ts +0 -88
- package/apps/studio/test/annotations-api.test.ts +0 -145
- package/apps/studio/test/annotations-bindings.test.ts +0 -262
- package/apps/studio/test/annotations-draw-modifiers.test.ts +0 -206
- package/apps/studio/test/annotations-groups.test.ts +0 -231
- package/apps/studio/test/annotations-layer.test.ts +0 -1011
- package/apps/studio/test/annotations-roundtrip.test.ts +0 -663
- package/apps/studio/test/annotations-snap.test.ts +0 -135
- package/apps/studio/test/annotations-text-format.test.ts +0 -396
- package/apps/studio/test/artboard-activity-overlay.test.tsx +0 -56
- package/apps/studio/test/artboard-guides-overlay.test.tsx +0 -152
- package/apps/studio/test/artboard-hug-commit.test.ts +0 -36
- package/apps/studio/test/artboard-kinds.test.tsx +0 -83
- package/apps/studio/test/artboard-selection-attrs.test.ts +0 -69
- package/apps/studio/test/asset-api.test.ts +0 -149
- package/apps/studio/test/assets-s3.test.ts +0 -249
- package/apps/studio/test/binary-smoke.test.ts +0 -47
- package/apps/studio/test/boot-self-heal.test.ts +0 -87
- package/apps/studio/test/browse-posture.test.tsx +0 -107
- package/apps/studio/test/bundle-smoke.test.ts +0 -46
- package/apps/studio/test/camera-reveal.test.tsx +0 -173
- package/apps/studio/test/canvas-artifacts.test.ts +0 -125
- package/apps/studio/test/canvas-build-sandbox.test.ts +0 -69
- package/apps/studio/test/canvas-build.test.ts +0 -141
- package/apps/studio/test/canvas-create-api.test.ts +0 -676
- package/apps/studio/test/canvas-cursors.test.ts +0 -164
- package/apps/studio/test/canvas-edit.test.ts +0 -470
- package/apps/studio/test/canvas-freshness-deps.test.ts +0 -64
- package/apps/studio/test/canvas-header.test.ts +0 -127
- package/apps/studio/test/canvas-hide-chrome.test.ts +0 -88
- package/apps/studio/test/canvas-hmr-runtime.test.tsx +0 -114
- package/apps/studio/test/canvas-lib-inline.test.ts +0 -146
- package/apps/studio/test/canvas-lib-motion.test.ts +0 -131
- package/apps/studio/test/canvas-lib-resolver.test.ts +0 -111
- package/apps/studio/test/canvas-list-watch.test.ts +0 -388
- package/apps/studio/test/canvas-media-drop.test.ts +0 -187
- package/apps/studio/test/canvas-meta-api.test.ts +0 -707
- package/apps/studio/test/canvas-move-api.test.ts +0 -587
- package/apps/studio/test/canvas-origin-gate.test.ts +0 -417
- package/apps/studio/test/canvas-pipeline.test.ts +0 -180
- package/apps/studio/test/canvas-rects.test.ts +0 -198
- package/apps/studio/test/canvas-reorder.test.ts +0 -211
- package/apps/studio/test/canvas-route.test.ts +0 -179
- package/apps/studio/test/canvas-shell-base.test.ts +0 -107
- package/apps/studio/test/canvas-shell-build-error.test.ts +0 -52
- package/apps/studio/test/canvas-url.test.ts +0 -138
- package/apps/studio/test/canvas-wheel-delta.test.ts +0 -42
- package/apps/studio/test/canvas-zoom-floor.test.ts +0 -73
- package/apps/studio/test/capture-core.test.ts +0 -32
- package/apps/studio/test/capture-determinism-shape.test.ts +0 -135
- package/apps/studio/test/chat-attachments.test.ts +0 -117
- package/apps/studio/test/chat-context.test.ts +0 -129
- package/apps/studio/test/chat-markdown.test.tsx +0 -58
- package/apps/studio/test/clip-addressing.test.ts +0 -738
- package/apps/studio/test/clip-ops.test.ts +0 -922
- package/apps/studio/test/cloud-build.test.ts +0 -95
- package/apps/studio/test/cloud-connect-note.test.ts +0 -135
- package/apps/studio/test/cloud-endpoints.test.ts +0 -590
- package/apps/studio/test/cloud-history-hardening.test.ts +0 -165
- package/apps/studio/test/cloud-history-posture.test.ts +0 -230
- package/apps/studio/test/cloud-managed-save-surfaces.test.ts +0 -90
- package/apps/studio/test/cloud-renew.test.ts +0 -205
- package/apps/studio/test/cloud-session-role.test.ts +0 -161
- package/apps/studio/test/cloud-shell-surfaces.test.ts +0 -170
- package/apps/studio/test/cold-start-apply.test.ts +0 -303
- package/apps/studio/test/collab-annotations-bridge.test.ts +0 -55
- package/apps/studio/test/collab-awareness-bridge.test.ts +0 -223
- package/apps/studio/test/collab-bridge.test.ts +0 -79
- package/apps/studio/test/collab-loopback.test.ts +0 -63
- package/apps/studio/test/collab-origin-gate.test.ts +0 -323
- package/apps/studio/test/collab-protocol.test.ts +0 -146
- package/apps/studio/test/collab-readonly-gate.test.ts +0 -194
- package/apps/studio/test/collab-reseed-guard.test.ts +0 -78
- package/apps/studio/test/collab-room.test.ts +0 -188
- package/apps/studio/test/collab-session-survival.test.tsx +0 -176
- package/apps/studio/test/collab-stress.test.ts +0 -129
- package/apps/studio/test/comment-mount.test.ts +0 -97
- package/apps/studio/test/comment-relay-origin-gate.test.ts +0 -117
- package/apps/studio/test/comments-api.test.ts +0 -277
- package/apps/studio/test/comments-fs-rebroadcast.test.ts +0 -155
- package/apps/studio/test/comments-overlay.test.ts +0 -117
- package/apps/studio/test/compile-entry.test.ts +0 -133
- package/apps/studio/test/component-map.test.ts +0 -48
- package/apps/studio/test/config-projection.test.ts +0 -104
- package/apps/studio/test/config-reload.test.ts +0 -230
- package/apps/studio/test/config-version.test.ts +0 -88
- package/apps/studio/test/context-resolve-tokens.test.ts +0 -97
- package/apps/studio/test/convert-to-absolute.test.ts +0 -333
- package/apps/studio/test/credential-grammar.test.ts +0 -107
- package/apps/studio/test/csp-canvas-shell.test.ts +0 -62
- package/apps/studio/test/csrf-write-guard.test.ts +0 -141
- package/apps/studio/test/debug-bundle.test.ts +0 -105
- package/apps/studio/test/design-setup-readiness.test.ts +0 -155
- package/apps/studio/test/detach-component.test.ts +0 -94
- package/apps/studio/test/dns-rebinding-guard.test.ts +0 -101
- package/apps/studio/test/dom-selection.test.ts +0 -198
- package/apps/studio/test/dynamic-text-edit.test.ts +0 -162
- package/apps/studio/test/edit-css-occurrence.test.ts +0 -81
- package/apps/studio/test/edit-persistence.test.ts +0 -91
- package/apps/studio/test/edit-scope-api.test.ts +0 -119
- package/apps/studio/test/edit-source-command.test.ts +0 -142
- package/apps/studio/test/edit-suppress.test.ts +0 -170
- package/apps/studio/test/editing-presence.test.ts +0 -103
- package/apps/studio/test/element-resize.test.ts +0 -136
- package/apps/studio/test/element-structural-api.test.ts +0 -434
- package/apps/studio/test/element-structural-edit.test.ts +0 -750
- package/apps/studio/test/ensure-browser.test.ts +0 -92
- package/apps/studio/test/equal-spacing-detector.test.ts +0 -257
- package/apps/studio/test/export-assemble.test.ts +0 -53
- package/apps/studio/test/export-browser-lane.test.ts +0 -126
- package/apps/studio/test/export-capture-fidelity.test.ts +0 -146
- package/apps/studio/test/export-capture-hygiene.test.ts +0 -675
- package/apps/studio/test/export-center.test.tsx +0 -287
- package/apps/studio/test/export-e2e-lanes.test.ts +0 -857
- package/apps/studio/test/export-format-scope-coherence.test.ts +0 -89
- package/apps/studio/test/export-lane.test.ts +0 -245
- package/apps/studio/test/export-shim-multi-capture.test.ts +0 -83
- package/apps/studio/test/exporters/canva.test.ts +0 -63
- package/apps/studio/test/exporters/degraded-propagation.test.ts +0 -123
- package/apps/studio/test/exporters/endpoint.test.ts +0 -121
- package/apps/studio/test/exporters/history.test.ts +0 -107
- package/apps/studio/test/exporters/html.test.ts +0 -26
- package/apps/studio/test/exporters/jobs.test.ts +0 -342
- package/apps/studio/test/exporters/pdf.test.ts +0 -85
- package/apps/studio/test/exporters/png.test.ts +0 -103
- package/apps/studio/test/exporters/pptx-deck.test.ts +0 -112
- package/apps/studio/test/exporters/pptx.test.ts +0 -31
- package/apps/studio/test/exporters/pw-launch.test.ts +0 -49
- package/apps/studio/test/exporters/runtime.test.ts +0 -59
- package/apps/studio/test/exporters/scope.test.ts +0 -292
- package/apps/studio/test/exporters/svg.test.ts +0 -29
- package/apps/studio/test/exporters/unsupported-media.test.ts +0 -123
- package/apps/studio/test/exporters/zip.test.ts +0 -104
- package/apps/studio/test/fetch-asset-gate.test.ts +0 -189
- package/apps/studio/test/figjam-v3-model.test.ts +0 -342
- package/apps/studio/test/figma-explode.test.ts +0 -438
- package/apps/studio/test/figma-provenance.test.ts +0 -108
- package/apps/studio/test/figma-routes.test.ts +0 -294
- package/apps/studio/test/file-lock.test.ts +0 -84
- package/apps/studio/test/fixtures/backgrounding-claude-auth.sh +0 -12
- package/apps/studio/test/fixtures/fake-claude-auth.mjs +0 -23
- package/apps/studio/test/fixtures/figjam-v3-groups-bindings.svg +0 -1
- package/apps/studio/test/fixtures/mock-acp-agent-caps.mjs +0 -158
- package/apps/studio/test/fixtures/mock-acp-agent-commands.mjs +0 -44
- package/apps/studio/test/fixtures/mock-acp-agent-elicit-flood.mjs +0 -46
- package/apps/studio/test/fixtures/mock-acp-agent-elicit-url.mjs +0 -42
- package/apps/studio/test/fixtures/mock-acp-agent-elicit.mjs +0 -63
- package/apps/studio/test/fixtures/mock-acp-agent-permission-flood.mjs +0 -51
- package/apps/studio/test/fixtures/mock-acp-agent-permission.mjs +0 -50
- package/apps/studio/test/fixtures/mock-acp-agent-slow.mjs +0 -45
- package/apps/studio/test/fixtures/mock-acp-agent-usage.mjs +0 -56
- package/apps/studio/test/fixtures/mock-acp-agent-wedged.mjs +0 -37
- package/apps/studio/test/fixtures/mock-acp-agent-write.mjs +0 -118
- package/apps/studio/test/fixtures/mock-acp-agent.mjs +0 -69
- package/apps/studio/test/fixtures/noisy-claude-auth.mjs +0 -27
- package/apps/studio/test/fixtures/perf-canvas.mjs +0 -201
- package/apps/studio/test/fixtures/phase-20-annotations.svg +0 -1
- package/apps/studio/test/fixtures/phase-21-annotations.svg +0 -1
- package/apps/studio/test/fixtures/tiny-subset.woff2 +0 -0
- package/apps/studio/test/fixtures/video-comp-fixture.tsx +0 -82
- package/apps/studio/test/fixtures/wedged-claude-auth.sh +0 -11
- package/apps/studio/test/footage-store.test.ts +0 -100
- package/apps/studio/test/fs-mkdir-api.test.ts +0 -248
- package/apps/studio/test/fs-watch.test.ts +0 -41
- package/apps/studio/test/gemma-models.test.ts +0 -245
- package/apps/studio/test/generate-route.test.ts +0 -106
- package/apps/studio/test/git-api.test.ts +0 -0
- package/apps/studio/test/git-branches.test.ts +0 -341
- package/apps/studio/test/git-cloud-posture.test.ts +0 -59
- package/apps/studio/test/git-grouping.test.ts +0 -106
- package/apps/studio/test/git-lifecycle.test.ts +0 -100
- package/apps/studio/test/git-log-format.test.ts +0 -95
- package/apps/studio/test/git-safe-rel.test.ts +0 -132
- package/apps/studio/test/git-system-engine.test.ts +0 -135
- package/apps/studio/test/git-watch.test.ts +0 -97
- package/apps/studio/test/github-api.test.ts +0 -577
- package/apps/studio/test/grid-track-handles.test.ts +0 -160
- package/apps/studio/test/handoff-static-frames.test.ts +0 -215
- package/apps/studio/test/handoff.test.ts +0 -329
- package/apps/studio/test/history-rollback.test.ts +0 -88
- package/apps/studio/test/hmr-broadcast.test.ts +0 -236
- package/apps/studio/test/hmr-classify.test.ts +0 -87
- package/apps/studio/test/hub-link.test.ts +0 -80
- package/apps/studio/test/identity-cache.test.ts +0 -83
- package/apps/studio/test/import-asset-browser.test.ts +0 -64
- package/apps/studio/test/import-asset-route.test.ts +0 -71
- package/apps/studio/test/import-asset.test.ts +0 -427
- package/apps/studio/test/import-brand-browser.test.ts +0 -88
- package/apps/studio/test/import-brand-route.test.ts +0 -120
- package/apps/studio/test/import-brand.test.ts +0 -206
- package/apps/studio/test/import-figma.test.ts +0 -667
- package/apps/studio/test/import-tokens.test.ts +0 -722
- package/apps/studio/test/index-data-canvas-ds.test.ts +0 -97
- package/apps/studio/test/input-router.test.ts +0 -757
- package/apps/studio/test/inspect-script-syntax.test.ts +0 -53
- package/apps/studio/test/inspect-selections.test.ts +0 -219
- package/apps/studio/test/knob-props-authored.test.ts +0 -87
- package/apps/studio/test/layers-synthetic-groups.test.ts +0 -96
- package/apps/studio/test/locator.test.ts +0 -214
- package/apps/studio/test/marquee-overlay.test.ts +0 -94
- package/apps/studio/test/media-commit-chain.test.ts +0 -210
- package/apps/studio/test/mode-toggle.test.tsx +0 -226
- package/apps/studio/test/motion-sample.test.ts +0 -35
- package/apps/studio/test/move-artboards-command.test.ts +0 -108
- package/apps/studio/test/participants-chrome.test.ts +0 -65
- package/apps/studio/test/paths.test.ts +0 -88
- package/apps/studio/test/pdf-print-boxes.test.ts +0 -359
- package/apps/studio/test/peer-selection-follows-camera.test.tsx +0 -131
- package/apps/studio/test/perf-harness.ts +0 -193
- package/apps/studio/test/phase-3.6-smoke.test.ts +0 -79
- package/apps/studio/test/photo-bg-remove-validation.test.ts +0 -71
- package/apps/studio/test/photo-canvas-bundle.test.ts +0 -79
- package/apps/studio/test/photo-edit-api.test.ts +0 -201
- package/apps/studio/test/photo-filters.test.ts +0 -149
- package/apps/studio/test/photo-pipeline.test.ts +0 -106
- package/apps/studio/test/photo-taxonomy.test.ts +0 -96
- package/apps/studio/test/print-marks.test.ts +0 -113
- package/apps/studio/test/print-units.test.ts +0 -173
- package/apps/studio/test/read-annotations.test.ts +0 -727
- package/apps/studio/test/read-only-gate.test.ts +0 -188
- package/apps/studio/test/read-only-role.test.ts +0 -95
- package/apps/studio/test/readiness.test.ts +0 -337
- package/apps/studio/test/remote-ahead-behind-cache.test.ts +0 -89
- package/apps/studio/test/reorder-api.test.ts +0 -210
- package/apps/studio/test/repo-concurrency.test.ts +0 -146
- package/apps/studio/test/repo-lock.test.ts +0 -350
- package/apps/studio/test/report-proxy.test.ts +0 -125
- package/apps/studio/test/ripple.test.ts +0 -197
- package/apps/studio/test/runtime-bundle-error-mapping.test.ts +0 -43
- package/apps/studio/test/runtime-bundle.test.ts +0 -69
- package/apps/studio/test/sanitize-annotation-svg.test.ts +0 -238
- package/apps/studio/test/scaffold-design.test.ts +0 -122
- package/apps/studio/test/server-lifecycle.test.ts +0 -38
- package/apps/studio/test/session-runtime-state.test.ts +0 -226
- package/apps/studio/test/session-scope.test.ts +0 -100
- package/apps/studio/test/shared-doc-cell-pairing.test.ts +0 -642
- package/apps/studio/test/shared-doc-convergence.test.ts +0 -265
- package/apps/studio/test/shared-doc-foundation.test.ts +0 -63
- package/apps/studio/test/shared-doc-migrate.test.ts +0 -369
- package/apps/studio/test/shared-doc-projection.test.ts +0 -238
- package/apps/studio/test/shell-importmap.test.ts +0 -56
- package/apps/studio/test/sizing-mode.test.ts +0 -102
- package/apps/studio/test/slash-commands.test.ts +0 -117
- package/apps/studio/test/snap-distance-pill.test.ts +0 -68
- package/apps/studio/test/spacing-handles.test.ts +0 -138
- package/apps/studio/test/specimen-select.test.ts +0 -88
- package/apps/studio/test/sync-agent.test.ts +0 -660
- package/apps/studio/test/sync-annotations-cold-start.test.ts +0 -302
- package/apps/studio/test/sync-asset-push.test.ts +0 -931
- package/apps/studio/test/sync-atomic-write.test.ts +0 -62
- package/apps/studio/test/sync-attach-incremental.test.ts +0 -527
- package/apps/studio/test/sync-autocommit.test.ts +0 -537
- package/apps/studio/test/sync-canvas-path.test.ts +0 -200
- package/apps/studio/test/sync-cell-write-nudge.test.ts +0 -346
- package/apps/studio/test/sync-codec.test.ts +0 -230
- package/apps/studio/test/sync-cold-seed-dedup.test.ts +0 -187
- package/apps/studio/test/sync-cold-start.test.ts +0 -304
- package/apps/studio/test/sync-connect-honesty.test.ts +0 -114
- package/apps/studio/test/sync-connection-state.test.ts +0 -337
- package/apps/studio/test/sync-ctl-channel.test.ts +0 -508
- package/apps/studio/test/sync-decide-file.test.ts +0 -420
- package/apps/studio/test/sync-doc-name.test.ts +0 -281
- package/apps/studio/test/sync-echo-guard.test.ts +0 -96
- package/apps/studio/test/sync-file-ledger.test.ts +0 -334
- package/apps/studio/test/sync-file-membership.test.ts +0 -347
- package/apps/studio/test/sync-file-plane.test.ts +0 -976
- package/apps/studio/test/sync-file-pull.test.ts +0 -333
- package/apps/studio/test/sync-fresh-link-parity.test.ts +0 -267
- package/apps/studio/test/sync-fs-mirror.test.ts +0 -181
- package/apps/studio/test/sync-hardening.test.ts +0 -586
- package/apps/studio/test/sync-hub-listing.test.ts +0 -46
- package/apps/studio/test/sync-hubs-config.test.ts +0 -133
- package/apps/studio/test/sync-incident-replay.test.ts +0 -211
- package/apps/studio/test/sync-journal.test.ts +0 -176
- package/apps/studio/test/sync-meta-codec.test.ts +0 -199
- package/apps/studio/test/sync-migrate-flat-fallback.test.ts +0 -98
- package/apps/studio/test/sync-move-retirement.test.ts +0 -231
- package/apps/studio/test/sync-panel-surface.test.ts +0 -153
- package/apps/studio/test/sync-path-pull.test.ts +0 -531
- package/apps/studio/test/sync-presentation.test.ts +0 -262
- package/apps/studio/test/sync-pull-budget.test.ts +0 -169
- package/apps/studio/test/sync-remote-docs.test.ts +0 -333
- package/apps/studio/test/sync-resync-routes.test.ts +0 -168
- package/apps/studio/test/sync-runtime.test.ts +0 -1675
- package/apps/studio/test/sync-seed-defers-to-hub.test.ts +0 -83
- package/apps/studio/test/sync-settings-routes.test.ts +0 -195
- package/apps/studio/test/sync-settings.test.ts +0 -151
- package/apps/studio/test/sync-status.test.ts +0 -261
- package/apps/studio/test/sync-supervisor.test.ts +0 -262
- package/apps/studio/test/sync-tombstone-apply.test.ts +0 -111
- package/apps/studio/test/sync-trash.test.ts +0 -132
- package/apps/studio/test/sync-two-peer-discovery.test.ts +0 -343
- package/apps/studio/test/sync-untrusted.test.ts +0 -103
- package/apps/studio/test/system-endpoint.test.ts +0 -144
- package/apps/studio/test/text-editability-stamp.test.ts +0 -131
- package/apps/studio/test/timeline-comp-target.test.ts +0 -139
- package/apps/studio/test/timeline-media-cache.test.ts +0 -41
- package/apps/studio/test/timeline-parse.test.ts +0 -184
- package/apps/studio/test/timeline-scale.test.ts +0 -65
- package/apps/studio/test/timeline-snap.test.ts +0 -85
- package/apps/studio/test/tool-palette-insert-anchor.test.ts +0 -86
- package/apps/studio/test/tool-palette.test.tsx +0 -55
- package/apps/studio/test/tour-overlay.test.tsx +0 -90
- package/apps/studio/test/trusted-request-host.test.ts +0 -66
- package/apps/studio/test/undo-sequence-byte-compare.test.ts +0 -211
- package/apps/studio/test/undo-stack.test.ts +0 -244
- package/apps/studio/test/use-agent-presence.test.tsx +0 -117
- package/apps/studio/test/use-annotation-resize.test.ts +0 -402
- package/apps/studio/test/use-annotation-selection.test.tsx +0 -77
- package/apps/studio/test/use-artboard-drag.test.ts +0 -324
- package/apps/studio/test/use-canvas-activity.test.tsx +0 -206
- package/apps/studio/test/use-collab.test.ts +0 -0
- package/apps/studio/test/use-cursor-modifiers.test.ts +0 -59
- package/apps/studio/test/use-keyboard-discipline.test.ts +0 -27
- package/apps/studio/test/use-selection-set.test.tsx +0 -166
- package/apps/studio/test/use-snap-guides.test.ts +0 -271
- package/apps/studio/test/use-tool-mode.test.tsx +0 -186
- package/apps/studio/test/use-undo-stack.test.tsx +0 -223
- package/apps/studio/test/video-asset.test.ts +0 -163
- package/apps/studio/test/video-comp-fixture.test.ts +0 -50
- package/apps/studio/test/video-comp.test.ts +0 -270
- package/apps/studio/test/video-encode-lib.test.ts +0 -63
- package/apps/studio/test/video-render-bridge.test.ts +0 -171
- package/apps/studio/test/whats-new.test.ts +0 -113
- package/apps/studio/test/whisper-setup.test.ts +0 -97
- package/apps/studio/test/workspace-containment.test.ts +0 -382
- package/apps/studio/test/workspace-signin.test.ts +0 -292
- package/apps/studio/test/ws-handshake.test.ts +0 -33
- package/cli/commands/cache.test.mjs +0 -166
- package/cli/commands/design-link.test.mjs +0 -419
- package/cli/commands/design.test.mjs +0 -240
- package/cli/commands/doctor.test.mjs +0 -237
- package/cli/commands/hub-workspace.test.mjs +0 -275
- package/cli/commands/hub.test.mjs +0 -225
- package/cli/commands/kg.test.mjs +0 -142
- package/cli/commands/preflight.test.mjs +0 -46
- package/cli/lib/argv.test.mjs +0 -46
- package/cli/lib/cache.test.mjs +0 -303
- package/cli/lib/cell-plan.test.mjs +0 -225
- package/cli/lib/config-lint.test.mjs +0 -117
- package/cli/lib/ddr-to-kgai.test.mjs +0 -118
- package/cli/lib/design-ownership.test.mjs +0 -329
- package/cli/lib/fetch-asset.test.mjs +0 -213
- package/cli/lib/figma-codegen-reachability.test.mjs +0 -104
- package/cli/lib/figma-import-controls.test.mjs +0 -70
- package/cli/lib/flow-design-integration.test.mjs +0 -165
- package/cli/lib/gitignore-block.test.mjs +0 -136
- package/cli/lib/gitignore-drift.test.mjs +0 -156
- package/cli/lib/hubs-config.test.mjs +0 -165
- package/cli/lib/pkg-root.test.mjs +0 -111
- package/cli/lib/plugin-cli-reachability.test.mjs +0 -57
- package/cli/lib/plugin-name-namespace.test.mjs +0 -71
- package/cli/lib/reconstruct-toolset.test.mjs +0 -194
- package/cli/lib/stack-detect.test.mjs +0 -121
- package/cli/lib/update-check.test.mjs +0 -32
- package/cli/lib/workspace-plan.test.mjs +0 -699
|
@@ -1,165 +0,0 @@
|
|
|
1
|
-
// DDR-185 security addendum, round 3 — the hardened agent-browser wrapper
|
|
2
|
-
// behind `maude design agent-browser-safe`
|
|
3
|
-
// (apps/studio/bin/_agent-browser-safe.mjs). This suite locks the SECURITY
|
|
4
|
-
// CORE: the closed subcommand allow-list (no cookies/clipboard/storage/
|
|
5
|
-
// connect/chat/eval reachable — `eval` was REMOVED in round 3, see below),
|
|
6
|
-
// the fixed positional-argument ARITY per subcommand with NO flag
|
|
7
|
-
// vocabulary at all, and the `--config`-pin + full env-clearing that closes
|
|
8
|
-
// the project-level `agent-browser.json` auto-discovery bypass a live
|
|
9
|
-
// adversarial pass found round 2 never considered.
|
|
10
|
-
|
|
11
|
-
import { describe, expect, test } from 'bun:test';
|
|
12
|
-
import { readFileSync } from 'node:fs';
|
|
13
|
-
import {
|
|
14
|
-
ALLOWED_SUBCOMMANDS,
|
|
15
|
-
CLEARED_ENV_KEYS,
|
|
16
|
-
SUBCOMMAND_ARITY,
|
|
17
|
-
validateArgv,
|
|
18
|
-
} from './_agent-browser-safe.mjs';
|
|
19
|
-
|
|
20
|
-
describe('validateArgv — subcommand allow-list', () => {
|
|
21
|
-
for (const sub of ALLOWED_SUBCOMMANDS) {
|
|
22
|
-
test(`accepts "${sub}" with its minimum expected argument count`, () => {
|
|
23
|
-
const [min] = SUBCOMMAND_ARITY[sub];
|
|
24
|
-
const args = Array.from({ length: min }, (_, i) => `arg${i}`);
|
|
25
|
-
expect(validateArgv([sub, ...args])).toBeNull();
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
test('round 3: "eval" is REMOVED — arbitrary JS execution can\'t be argv-constrained (ethical-hacker Finding 1)', () => {
|
|
30
|
-
// Round 2 kept eval on the allow-list; a live adversarial pass proved
|
|
31
|
-
// eval defeats --allowed-domains entirely (window.location.href escapes
|
|
32
|
-
// the navigation gate; a no-cors fetch() exfiltrates without even
|
|
33
|
-
// needing a domain match) — no argv check can close that, so the
|
|
34
|
-
// capability itself is cut, mirroring this DDR's own `find` precedent.
|
|
35
|
-
expect(ALLOWED_SUBCOMMANDS).not.toContain('eval');
|
|
36
|
-
const reason = validateArgv(['eval', '1+1']);
|
|
37
|
-
expect(reason).toContain('eval');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
for (const sub of [
|
|
41
|
-
'cookies',
|
|
42
|
-
'clipboard',
|
|
43
|
-
'storage',
|
|
44
|
-
'connect',
|
|
45
|
-
'chat',
|
|
46
|
-
'upload',
|
|
47
|
-
'auth',
|
|
48
|
-
'profiles',
|
|
49
|
-
]) {
|
|
50
|
-
test(`rejects the credential/session-hijack-capable subcommand "${sub}" (ethical-hacker Finding D)`, () => {
|
|
51
|
-
const reason = validateArgv([sub, 'get']);
|
|
52
|
-
expect(reason).toContain(sub);
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
test('rejects when no subcommand is given', () => {
|
|
57
|
-
expect(validateArgv([])).not.toBeNull();
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe('validateArgv — fixed arity, NO flag vocabulary at all', () => {
|
|
62
|
-
test('rejects an extra argument beyond the expected arity, whatever it looks like', () => {
|
|
63
|
-
const reason = validateArgv(['open', 'http://localhost:1', 'extra']);
|
|
64
|
-
expect(reason).toContain('exactly 1');
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test('rejects the --provider/-p remote-cloud-browser PoC verbatim (round-2 finding)', () => {
|
|
68
|
-
expect(
|
|
69
|
-
validateArgv(['open', 'http://localhost:1', '--provider', 'browserbase'])
|
|
70
|
-
).not.toBeNull();
|
|
71
|
-
expect(validateArgv(['open', '-p', 'browserbase', 'http://localhost:1'])).not.toBeNull();
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test('rejects the persistent-profile PoC verbatim (still closed under the arity model)', () => {
|
|
75
|
-
expect(
|
|
76
|
-
validateArgv(['open', '--profile', 'Default', 'https://github.com/settings/tokens'])
|
|
77
|
-
).not.toBeNull();
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test('rejects the CDP-attach PoC verbatim (still closed under the arity model)', () => {
|
|
81
|
-
expect(validateArgv(['snapshot', '--cdp', '9222'])).not.toBeNull();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
test('a positional value that itself starts with "-" is NOT mistaken for a flag (arity-based, not prefix-based)', () => {
|
|
85
|
-
// agent-browser-safe get "-weird-thing" must still work — the arity
|
|
86
|
-
// model looks at COUNT, not at whether a token happens to start with a
|
|
87
|
-
// dash.
|
|
88
|
-
expect(validateArgv(['get', '-weird-thing'])).toBeNull();
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test('screenshot accepts 0 or 1 positional args (optional path)', () => {
|
|
92
|
-
expect(validateArgv(['screenshot'])).toBeNull();
|
|
93
|
-
expect(validateArgv(['screenshot', '/tmp/out.png'])).toBeNull();
|
|
94
|
-
expect(validateArgv(['screenshot', '/tmp/out.png', 'extra'])).not.toBeNull();
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test('get accepts 1 or 2 positional args (thing + optional selector)', () => {
|
|
98
|
-
expect(validateArgv(['get', 'text'])).toBeNull();
|
|
99
|
-
expect(validateArgv(['get', 'text', '.selector'])).toBeNull();
|
|
100
|
-
expect(validateArgv(['get'])).not.toBeNull();
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
test('snapshot and close accept exactly 0 arguments', () => {
|
|
104
|
-
expect(validateArgv(['snapshot'])).toBeNull();
|
|
105
|
-
expect(validateArgv(['snapshot', '-i'])).not.toBeNull();
|
|
106
|
-
expect(validateArgv(['close'])).toBeNull();
|
|
107
|
-
expect(validateArgv(['close', '--all'])).not.toBeNull();
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test('an ordinary allowed invocation with no extra args passes', () => {
|
|
111
|
-
expect(validateArgv(['open', 'http://localhost:3000/'])).toBeNull();
|
|
112
|
-
expect(validateArgv(['screenshot', '/tmp/out.png'])).toBeNull();
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
describe('CLEARED_ENV_KEYS', () => {
|
|
117
|
-
test('round 3: covers AGENT_BROWSER_CONFIG (defense in depth alongside the --config pin)', () => {
|
|
118
|
-
expect(CLEARED_ENV_KEYS).toContain('AGENT_BROWSER_CONFIG');
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
test('round 2: covers the three vars a live verification pass found missing from round 1', () => {
|
|
122
|
-
for (const key of [
|
|
123
|
-
'AGENT_BROWSER_AUTO_CONNECT',
|
|
124
|
-
'AGENT_BROWSER_PROXY',
|
|
125
|
-
'AGENT_BROWSER_ENGINE',
|
|
126
|
-
]) {
|
|
127
|
-
expect(CLEARED_ENV_KEYS).toContain(key);
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
|
|
131
|
-
test('covers all four standard proxy env vars, both cases', () => {
|
|
132
|
-
for (const key of [
|
|
133
|
-
'HTTP_PROXY',
|
|
134
|
-
'HTTPS_PROXY',
|
|
135
|
-
'ALL_PROXY',
|
|
136
|
-
'http_proxy',
|
|
137
|
-
'https_proxy',
|
|
138
|
-
'all_proxy',
|
|
139
|
-
]) {
|
|
140
|
-
expect(CLEARED_ENV_KEYS).toContain(key);
|
|
141
|
-
}
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
test('still covers the original round-1 set', () => {
|
|
145
|
-
for (const key of [
|
|
146
|
-
'AGENT_BROWSER_PROFILE',
|
|
147
|
-
'AGENT_BROWSER_SESSION_NAME',
|
|
148
|
-
'AGENT_BROWSER_ACTION_POLICY',
|
|
149
|
-
'AGENT_BROWSER_CONFIRM_ACTIONS',
|
|
150
|
-
'AGENT_BROWSER_CONFIRM_INTERACTIVE',
|
|
151
|
-
]) {
|
|
152
|
-
expect(CLEARED_ENV_KEYS).toContain(key);
|
|
153
|
-
}
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
describe('the forced --config target (round 3: closes ./agent-browser.json auto-discovery)', () => {
|
|
158
|
-
test('_agent-browser-safe-config.json exists and is valid, empty JSON', () => {
|
|
159
|
-
// Must stay valid JSON — agent-browser exits with an error if --config
|
|
160
|
-
// points to a missing/invalid file, which would break every allowed
|
|
161
|
-
// subcommand, not just fail safe.
|
|
162
|
-
const raw = readFileSync(new URL('./_agent-browser-safe-config.json', import.meta.url), 'utf8');
|
|
163
|
-
expect(JSON.parse(raw)).toEqual({});
|
|
164
|
-
});
|
|
165
|
-
});
|
|
@@ -1,280 +0,0 @@
|
|
|
1
|
-
// DDR-185 — the loopback-only curl gate behind `maude design curl-local`
|
|
2
|
-
// (apps/studio/bin/_curl-local.mjs). This suite locks the SECURITY CORE: the
|
|
3
|
-
// strict-loopback address classifier (accepts ONLY 127.0.0.0/8 / ::1 / the
|
|
4
|
-
// IPv4-mapped form — unlike `_fetch-asset.mjs`'s `classifyAddress`, an
|
|
5
|
-
// ordinary private-LAN address like 192.168.1.1 is REJECTED here, not
|
|
6
|
-
// allowed), the every-record DNS check (closes the multi-A-record rebinding
|
|
7
|
-
// gap), and — round 2 of the security addendum — the Maude-owned request
|
|
8
|
-
// parser (`parseRequestArgv`) that REPLACED the raw-curl-argv allowlist a
|
|
9
|
-
// live security fan-out found still bypassable via curl's own concatenated
|
|
10
|
-
// short-flag syntax (`-K<path>`, `-x<url>`, `-T<path>` all slipped past the
|
|
11
|
-
// `a.split('=')[0]` check) and a missing `--noproxy` reject entry. This file
|
|
12
|
-
// no longer recognizes ANY curl flag at all — only its own small vocabulary
|
|
13
|
-
// — so there is no curl-syntax bypass class left to enumerate against.
|
|
14
|
-
|
|
15
|
-
import { describe, expect, test } from 'bun:test';
|
|
16
|
-
import {
|
|
17
|
-
buildCurlArgs,
|
|
18
|
-
CurlLocalArgvError,
|
|
19
|
-
CurlLocalError,
|
|
20
|
-
classifyRecords,
|
|
21
|
-
isLoopbackAddress,
|
|
22
|
-
parseRequestArgv,
|
|
23
|
-
resolveLoopbackIp,
|
|
24
|
-
} from './_curl-local.mjs';
|
|
25
|
-
|
|
26
|
-
describe('isLoopbackAddress', () => {
|
|
27
|
-
test('accepts IPv4 loopback (127.0.0.0/8, not just 127.0.0.1)', () => {
|
|
28
|
-
expect(isLoopbackAddress('127.0.0.1')).toBe(true);
|
|
29
|
-
expect(isLoopbackAddress('127.9.9.9')).toBe(true);
|
|
30
|
-
expect(isLoopbackAddress('127.255.255.255')).toBe(true);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
test('accepts IPv6 ::1 and the IPv4-mapped loopback form', () => {
|
|
34
|
-
expect(isLoopbackAddress('::1')).toBe(true);
|
|
35
|
-
expect(isLoopbackAddress('::ffff:127.0.0.1')).toBe(true);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test("rejects ordinary private-LAN addresses — this is STRICTER than fetch-asset's classifier", () => {
|
|
39
|
-
// fetch-asset's classifyAddress blocks these too (as "not a safe egress
|
|
40
|
-
// target"), but for a different reason (SSRF from an external URL). Here
|
|
41
|
-
// the bar is "is this literally my own machine" — a private-LAN IP is a
|
|
42
|
-
// real, different host, not loopback, and must still prompt.
|
|
43
|
-
expect(isLoopbackAddress('192.168.1.1')).toBe(false);
|
|
44
|
-
expect(isLoopbackAddress('10.0.0.1')).toBe(false);
|
|
45
|
-
expect(isLoopbackAddress('172.16.0.1')).toBe(false);
|
|
46
|
-
expect(isLoopbackAddress('169.254.169.254')).toBe(false); // cloud IMDS
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
test('rejects public addresses and link-local/multicast/reserved ranges', () => {
|
|
50
|
-
for (const ip of ['8.8.8.8', '1.1.1.1', '224.0.0.1', '240.0.0.1', 'fe80::1']) {
|
|
51
|
-
expect(isLoopbackAddress(ip)).toBe(false);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test('rejects a non-IP string', () => {
|
|
56
|
-
expect(isLoopbackAddress('not-an-ip')).toBe(false);
|
|
57
|
-
expect(isLoopbackAddress('localhost')).toBe(false); // a hostname, not a literal — resolved separately
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe('classifyRecords — the every-record check (multi-A-record DNS rebinding)', () => {
|
|
62
|
-
test('all-loopback records pass', () => {
|
|
63
|
-
expect(
|
|
64
|
-
classifyRecords('localhost', [{ address: '127.0.0.1' }, { address: '127.0.0.2' }])
|
|
65
|
-
).toBeNull();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
test('a single non-loopback record among several loopback ones still fails the whole host', () => {
|
|
69
|
-
const reason = classifyRecords('rebind.example', [
|
|
70
|
-
{ address: '127.0.0.1' },
|
|
71
|
-
{ address: '93.184.216.34' },
|
|
72
|
-
]);
|
|
73
|
-
expect(reason).toContain('93.184.216.34');
|
|
74
|
-
expect(reason).toContain('non-loopback');
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
test('empty record set fails', () => {
|
|
78
|
-
expect(classifyRecords('empty.example', [])).toContain('no DNS records');
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
describe('resolveLoopbackIp', () => {
|
|
83
|
-
test('an IP-literal host skips DNS entirely — loopback passes, returns itself', async () => {
|
|
84
|
-
await expect(resolveLoopbackIp('127.0.0.1')).resolves.toBe('127.0.0.1');
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test('an IP-literal host that is not loopback throws CurlLocalError(3)', async () => {
|
|
88
|
-
await expect(resolveLoopbackIp('8.8.8.8')).rejects.toThrow(CurlLocalError);
|
|
89
|
-
try {
|
|
90
|
-
await resolveLoopbackIp('8.8.8.8');
|
|
91
|
-
throw new Error('should have thrown');
|
|
92
|
-
} catch (err) {
|
|
93
|
-
expect(err.code).toBe(3);
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test('a hostname resolving to all-loopback records (injected lookup) passes, returns the pin IP', async () => {
|
|
98
|
-
const lookupFn = async () => [
|
|
99
|
-
{ address: '127.0.0.1', family: 4 },
|
|
100
|
-
{ address: '::1', family: 6 },
|
|
101
|
-
];
|
|
102
|
-
await expect(resolveLoopbackIp('localhost', { lookupFn })).resolves.toBe('127.0.0.1');
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
test('a hostname with ONE non-loopback record among several is rejected (DNS-rebinding defense)', async () => {
|
|
106
|
-
const lookupFn = async () => [
|
|
107
|
-
{ address: '127.0.0.1', family: 4 },
|
|
108
|
-
{ address: '203.0.113.5', family: 4 }, // TEST-NET-3 — a real "other" address
|
|
109
|
-
];
|
|
110
|
-
await expect(resolveLoopbackIp('rebind.example', { lookupFn })).rejects.toThrow(CurlLocalError);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test('DNS resolution failure is a rejection, not a silent pass', async () => {
|
|
114
|
-
const lookupFn = async () => {
|
|
115
|
-
const err = new Error('getaddrinfo ENOTFOUND nope.invalid');
|
|
116
|
-
err.code = 'ENOTFOUND';
|
|
117
|
-
throw err;
|
|
118
|
-
};
|
|
119
|
-
await expect(resolveLoopbackIp('nope.invalid', { lookupFn })).rejects.toThrow(CurlLocalError);
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
describe('parseRequestArgv — the Maude-owned flag vocabulary (never raw curl syntax)', () => {
|
|
124
|
-
test('a bare URL parses as a GET with no headers/body', () => {
|
|
125
|
-
const req = parseRequestArgv(['http://localhost:3000/api']);
|
|
126
|
-
expect(req.url).toBe('http://localhost:3000/api');
|
|
127
|
-
expect(req.method).toBe('GET');
|
|
128
|
-
expect(req.headers).toEqual([]);
|
|
129
|
-
expect(req.data).toBeNull();
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
test('--method, repeated --header, and --data all parse', () => {
|
|
133
|
-
const req = parseRequestArgv([
|
|
134
|
-
'http://localhost:3000/api',
|
|
135
|
-
'--method',
|
|
136
|
-
'post',
|
|
137
|
-
'--header',
|
|
138
|
-
'Content-Type: application/json',
|
|
139
|
-
'--header',
|
|
140
|
-
'X-Test: 1',
|
|
141
|
-
'--data',
|
|
142
|
-
'{"a":1}',
|
|
143
|
-
]);
|
|
144
|
-
expect(req.method).toBe('POST'); // uppercased
|
|
145
|
-
expect(req.headers).toEqual(['Content-Type: application/json', 'X-Test: 1']);
|
|
146
|
-
expect(req.data).toBe('{"a":1}');
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
test('boolean flags (--insecure/--include/--verbose) parse with no value consumed', () => {
|
|
150
|
-
const req = parseRequestArgv([
|
|
151
|
-
'http://localhost:3000/',
|
|
152
|
-
'--insecure',
|
|
153
|
-
'--include',
|
|
154
|
-
'--verbose',
|
|
155
|
-
]);
|
|
156
|
-
expect(req.insecure).toBe(true);
|
|
157
|
-
expect(req.include).toBe(true);
|
|
158
|
-
expect(req.verbose).toBe(true);
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
test('rejects an unrecognized method', () => {
|
|
162
|
-
expect(() => parseRequestArgv(['http://localhost:3000/', '--method', 'TRACE'])).toThrow(
|
|
163
|
-
CurlLocalArgvError
|
|
164
|
-
);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
test('rejects a malformed header (must look like "Name: value")', () => {
|
|
168
|
-
expect(() => parseRequestArgv(['http://localhost:3000/', '--header', 'not-a-header'])).toThrow(
|
|
169
|
-
CurlLocalArgvError
|
|
170
|
-
);
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
test('rejects a non-positive --max-time', () => {
|
|
174
|
-
expect(() => parseRequestArgv(['http://localhost:3000/', '--max-time', '0'])).toThrow(
|
|
175
|
-
CurlLocalArgvError
|
|
176
|
-
);
|
|
177
|
-
expect(() => parseRequestArgv(['http://localhost:3000/', '--max-time', 'nope'])).toThrow(
|
|
178
|
-
CurlLocalArgvError
|
|
179
|
-
);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
test('rejects a non-http(s) URL scheme', () => {
|
|
183
|
-
expect(() => parseRequestArgv(['file:///etc/passwd'])).toThrow(CurlLocalArgvError);
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
test('rejects a value-taking flag with no value', () => {
|
|
187
|
-
expect(() => parseRequestArgv(['http://localhost:3000/', '--header'])).toThrow(
|
|
188
|
-
CurlLocalArgvError
|
|
189
|
-
);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
test('rejects more than one positional argument', () => {
|
|
193
|
-
expect(() => parseRequestArgv(['http://localhost:3000/', 'http://localhost:4000/'])).toThrow(
|
|
194
|
-
CurlLocalArgvError
|
|
195
|
-
);
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
test('rejects when no URL is given at all', () => {
|
|
199
|
-
expect(() => parseRequestArgv(['--method', 'GET'])).toThrow(CurlLocalArgvError);
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
// ── Round-2 security addendum: these are exactly the argv forms that
|
|
203
|
-
// bypassed the PREVIOUS raw-curl-argv allowlist. None of them are
|
|
204
|
-
// "rejected flags" here — they're simply not part of this file's flag
|
|
205
|
-
// vocabulary at all, so they fall through to the generic
|
|
206
|
-
// "unrecognized flag" / "unexpected extra argument" path.
|
|
207
|
-
test('rejects the concatenated -K<path> config-file-smuggling PoC verbatim (round-2 finding #1)', () => {
|
|
208
|
-
expect(() => parseRequestArgv(['http://127.0.0.1:1/', '-Kevil.conf'])).toThrow(
|
|
209
|
-
CurlLocalArgvError
|
|
210
|
-
);
|
|
211
|
-
});
|
|
212
|
-
|
|
213
|
-
test('rejects a caller-supplied --noproxy override verbatim (round-2 finding #2)', () => {
|
|
214
|
-
expect(() => parseRequestArgv(['http://127.0.0.1:1/', '--noproxy', ''])).toThrow(
|
|
215
|
-
CurlLocalArgvError
|
|
216
|
-
);
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
test('rejects the concatenated -x<url>/-T<path> short forms (round-2 finding #3)', () => {
|
|
220
|
-
expect(() => parseRequestArgv(['-xhttp://evil.example', 'http://127.0.0.1:1/'])).toThrow(
|
|
221
|
-
CurlLocalArgvError
|
|
222
|
-
);
|
|
223
|
-
expect(() => parseRequestArgv(['http://127.0.0.1:1/', '-T/etc/passwd'])).toThrow(
|
|
224
|
-
CurlLocalArgvError
|
|
225
|
-
);
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
test('rejects the original --resolve/--connect-to target-override PoC verbatim', () => {
|
|
229
|
-
expect(() =>
|
|
230
|
-
parseRequestArgv(['--resolve', 'localhost:80:203.0.113.5', 'http://localhost/'])
|
|
231
|
-
).toThrow(CurlLocalArgvError);
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
test('a value containing curl-flag-shaped text is inert data, not a flag — no injection via --data', () => {
|
|
235
|
-
// "@/etc/passwd" as a --data value must never be treated as curl's
|
|
236
|
-
// "read a file" shorthand (that's -d/--data's behavior, not
|
|
237
|
-
// --data-raw's, which buildCurlArgs always uses — see the live
|
|
238
|
-
// end-to-end test below for the full round-trip proof).
|
|
239
|
-
const req = parseRequestArgv([
|
|
240
|
-
'http://localhost:3000/',
|
|
241
|
-
'--data',
|
|
242
|
-
'@/etc/passwd; -K evil.conf',
|
|
243
|
-
]);
|
|
244
|
-
expect(req.data).toBe('@/etc/passwd; -K evil.conf');
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
describe("buildCurlArgs — every flag name is one of THIS file's own hardcoded literals", () => {
|
|
249
|
-
test('a bare GET pins the connection and forces the safety flags', () => {
|
|
250
|
-
const req = parseRequestArgv(['http://localhost:3000/']);
|
|
251
|
-
const args = buildCurlArgs(req, '127.0.0.1', 3000);
|
|
252
|
-
expect(args).toContain('--resolve');
|
|
253
|
-
expect(args[args.indexOf('--resolve') + 1]).toBe('localhost:3000:127.0.0.1');
|
|
254
|
-
expect(args).toContain('--noproxy');
|
|
255
|
-
expect(args).toContain('-q'); // curlrc disabled
|
|
256
|
-
expect(args).toEqual(expect.arrayContaining(['--max-redirs', '0']));
|
|
257
|
-
expect(args.at(-1)).toBe('http://localhost:3000/'); // the URL, always last, after `--`
|
|
258
|
-
expect(args.at(-2)).toBe('--');
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
test('POST + headers + data map to -X/-H/--data-raw (never -d, which treats a leading @ as a file path)', () => {
|
|
262
|
-
const req = parseRequestArgv([
|
|
263
|
-
'http://localhost:3000/api',
|
|
264
|
-
'--method',
|
|
265
|
-
'POST',
|
|
266
|
-
'--header',
|
|
267
|
-
'Content-Type: application/json',
|
|
268
|
-
'--data',
|
|
269
|
-
'@/etc/passwd',
|
|
270
|
-
]);
|
|
271
|
-
const args = buildCurlArgs(req, '127.0.0.1', 3000);
|
|
272
|
-
expect(args).toContain('-X');
|
|
273
|
-
expect(args[args.indexOf('-X') + 1]).toBe('POST');
|
|
274
|
-
expect(args).toContain('-H');
|
|
275
|
-
expect(args[args.indexOf('-H') + 1]).toBe('Content-Type: application/json');
|
|
276
|
-
expect(args).toContain('--data-raw');
|
|
277
|
-
expect(args).not.toContain('-d');
|
|
278
|
-
expect(args[args.indexOf('--data-raw') + 1]).toBe('@/etc/passwd');
|
|
279
|
-
});
|
|
280
|
-
});
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
// _pw-launch.test.mjs — the pure, security-relevant helpers shared by the
|
|
2
|
-
// multi-artboard export shims (_png/_pdf-playwright.mjs). Regression coverage
|
|
3
|
-
// for the arbitrary-file-write finding fixed in the print-artboards dogfood
|
|
4
|
-
// pass (DDR-182 addendum): a `data-dc-screen` id is DDR-054 untrusted canvas
|
|
5
|
-
// content, and used to be trusted verbatim as a `path.join` segment.
|
|
6
|
-
|
|
7
|
-
import { describe, expect, test } from 'bun:test';
|
|
8
|
-
import { assertRenderOutputSizeOk, safeArtboardFilename } from './_pw-launch.mjs';
|
|
9
|
-
|
|
10
|
-
describe('safeArtboardFilename', () => {
|
|
11
|
-
test('passes a well-formed id through unchanged', () => {
|
|
12
|
-
expect(safeArtboardFilename('ak-alpha', 0, 'pdf')).toBe('ak-alpha.pdf');
|
|
13
|
-
expect(safeArtboardFilename('Beta_2', 3, 'png')).toBe('Beta_2.png');
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test('falls back to the positional name for a path-traversal id', () => {
|
|
17
|
-
expect(safeArtboardFilename('../../../../tmp/pwned', 2, 'pdf')).toBe('artboard-3.pdf');
|
|
18
|
-
expect(safeArtboardFilename('/etc/passwd', 0, 'png')).toBe('artboard-1.png');
|
|
19
|
-
expect(safeArtboardFilename('..', 1, 'pdf')).toBe('artboard-2.pdf');
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test('falls back for null/undefined/empty/non-string ids', () => {
|
|
23
|
-
expect(safeArtboardFilename(null, 0, 'pdf')).toBe('artboard-1.pdf');
|
|
24
|
-
expect(safeArtboardFilename(undefined, 4, 'png')).toBe('artboard-5.png');
|
|
25
|
-
expect(safeArtboardFilename('', 1, 'pdf')).toBe('artboard-2.pdf');
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test('falls back for an id starting with a digit or containing unsafe chars', () => {
|
|
29
|
-
expect(safeArtboardFilename('1abc', 0, 'pdf')).toBe('artboard-1.pdf');
|
|
30
|
-
expect(safeArtboardFilename('a/b', 0, 'pdf')).toBe('artboard-1.pdf');
|
|
31
|
-
expect(safeArtboardFilename('a b', 0, 'pdf')).toBe('artboard-1.pdf');
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
test('falls back for an id over the 64-char cap', () => {
|
|
35
|
-
expect(safeArtboardFilename(`a${'x'.repeat(64)}`, 0, 'pdf')).toBe('artboard-1.pdf');
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
describe('assertRenderOutputSizeOk', () => {
|
|
40
|
-
test('does not exit for a reasonable A4-at-300dpi request', () => {
|
|
41
|
-
const origExit = process.exit;
|
|
42
|
-
let exited = false;
|
|
43
|
-
process.exit = () => {
|
|
44
|
-
exited = true;
|
|
45
|
-
};
|
|
46
|
-
try {
|
|
47
|
-
assertRenderOutputSizeOk(818, 1146, 3.125, '_test');
|
|
48
|
-
} finally {
|
|
49
|
-
process.exit = origExit;
|
|
50
|
-
}
|
|
51
|
-
expect(exited).toBe(false);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
test('exits when the requested output exceeds the max side guard', () => {
|
|
55
|
-
const origExit = process.exit;
|
|
56
|
-
const origError = console.error;
|
|
57
|
-
let exitCode;
|
|
58
|
-
process.exit = (code) => {
|
|
59
|
-
exitCode = code;
|
|
60
|
-
throw new Error('exit'); // stop the (test-only) function from continuing past exit
|
|
61
|
-
};
|
|
62
|
-
console.error = () => {};
|
|
63
|
-
try {
|
|
64
|
-
expect(() => assertRenderOutputSizeOk(20000, 20000, 1, '_test')).toThrow();
|
|
65
|
-
} finally {
|
|
66
|
-
process.exit = origExit;
|
|
67
|
-
console.error = origError;
|
|
68
|
-
}
|
|
69
|
-
expect(exitCode).toBe(1);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
test('exits when the requested output exceeds the byte-size guard even under the side cap', () => {
|
|
73
|
-
const origExit = process.exit;
|
|
74
|
-
const origError = console.error;
|
|
75
|
-
let exitCode;
|
|
76
|
-
process.exit = (code) => {
|
|
77
|
-
exitCode = code;
|
|
78
|
-
throw new Error('exit');
|
|
79
|
-
};
|
|
80
|
-
console.error = () => {};
|
|
81
|
-
try {
|
|
82
|
-
// 15000×15000 stays under the 16000px side cap but blows the ~600MB estimate.
|
|
83
|
-
expect(() => assertRenderOutputSizeOk(15000, 15000, 1, '_test')).toThrow();
|
|
84
|
-
} finally {
|
|
85
|
-
process.exit = origExit;
|
|
86
|
-
console.error = origError;
|
|
87
|
-
}
|
|
88
|
-
expect(exitCode).toBe(1);
|
|
89
|
-
});
|
|
90
|
-
});
|