Fix four deployability blockers found by QA (closes #189, closes #190, closes #191, closes #192)

- CSRF over plain HTTP (#189): gorilla/csrf assumed https for its
  same-origin check, so setup and every POST returned 403 over plain
  HTTP. Gate csrf.PlaintextHTTPRequest on a new UPAAS_PLAINTEXT_HTTP
  config value; the default keeps https, correct for a TLS-terminating
  reverse proxy. The README plain-HTTP recipe now sets it.
- git image never pulled (#190): ensureImage pulls alpine/git (pinned
  digest unchanged) when absent, before the clone container is created.
- port-mapping 500 (#192): the ports delete form used {{ .CSRFField }}
  inside {{range .Ports}}, where the dot is a *models.Port; use
  {{ $.CSRFField }} like the labels and volumes blocks.
- env-var 403 (#191): the editor read the CSRF token from $el (the
  submitting form, which has none) instead of $root, sending an empty
  token; read from $root.

Model: opus-4-8
This commit is contained in:
2026-09-09 14:12:09 +00:00
parent 7a34fc999c
commit cdcf527b25
8 changed files with 149 additions and 4 deletions

View File

@@ -667,10 +667,51 @@ func (c *Client) performClone(
return c.runGitClone(ctx, gitContainerID)
}
// ensureImage pulls ref if it is not already present locally. The pinned
// digest is preserved: a pull of an image already present is a no-op, and a
// missing one is fetched before it is used to create a container.
func (c *Client) ensureImage(ctx context.Context, ref string) error {
_, _, err := c.docker.ImageInspectWithRaw(ctx, ref)
if err == nil {
return nil
}
if !client.IsErrNotFound(err) {
return fmt.Errorf("failed to inspect image %s: %w", ref, err)
}
c.log.Info("pulling image", "image", ref)
reader, err := c.docker.ImagePull(ctx, ref, image.PullOptions{})
if err != nil {
return fmt.Errorf("failed to pull image %s: %w", ref, err)
}
defer func() {
closeErr := reader.Close()
if closeErr != nil {
c.log.Error("failed to close image pull reader", "error", closeErr)
}
}()
// The pull only completes once its response stream is fully drained.
_, err = io.Copy(io.Discard, reader)
if err != nil {
return fmt.Errorf("failed to pull image %s: %w", ref, err)
}
return nil
}
func (c *Client) createGitContainer(
ctx context.Context,
cfg *cloneConfig,
) (ContainerID, error) {
err := c.ensureImage(ctx, gitImage)
if err != nil {
return "", err
}
gitSSHCmd := "ssh -i /keys/deploy_key -o StrictHostKeyChecking=no"
// Build the git command using environment variables to avoid shell injection.