Commit Graph
23 Commits
Author SHA1 Message Date
clawbot 978356a672 Tag built images with the commit's short hash (closes #239)
Check / check (pull_request) Skipped
Builds are tagged upaas-<app>:<short hash>, git's own short form of
the commit checked out, instead of the deployment number.

A redeploy of a commit gives the tag to the new image. The cleanup
after a deploy now also finds the app's untagged images among those
its deployments recorded, and removes them by ID once the app neither
runs them nor would roll back to them. It keeps every image any app
uses, since apps that build the same commit can share one.

The clone reads the commits from git's output after removing Docker's
log headers, which had hidden the COMMIT: line; commit_sha is now
saved on update so manual deploys keep the commit they recorded.

Model: opus-5-5
2026-09-29 11:15:22 +00:00
clawbot 9754b73f27 Widen the app page, double its log heights and move the logs (closes #246)
Check / check (pull_request) Successful in 5m29s
The app page's content column is now at most 84rem wide instead of
56rem (max-w-4xl), set inline because the committed Tailwind CSS has no
class for that width. The build log and container log boxes are 800px
tall instead of 400px. The build log section moves to between the
webhook URL and the environment variables, and the container log
section moves to directly above the deploy key; nothing else moves. A
new handler test renders the app page and checks the width, the section
order and both log heights.
Disclosure: merged after a rebase that changed only TODO.md; the review gated this change on the next before #234, which touches no template.

Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
2026-09-29 12:46:47 +02:00
clawbot 679c80700f Report the build's own error and refuse daemons too old for BuildKit (closes #234)
Check / check (pull_request) Successful in 4m23s
A build whose output ends in Docker's error line now fails with that
error, instead of going on to inspect a tag that was never created. The
build output is written to the deployment log before the failure is
recorded, so the log ends in order. Before building, upaas compares the
daemon's API version with 1.39 (Docker Engine 18.09), the first that
builds with BuildKit without experimental mode, and fails the deploy on
an older daemon instead of letting it use the legacy builder. The
README's Compose section gives the update command and the Docker Engine
versions builds need.
Disclosure: merged after a rebase that changed only TODO.md; the review gated this tree on the current next.

Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
2026-09-29 12:42:55 +02:00
clawbot 211e2a4a5a Show the deploy branch in the app page title (closes #240)
Check / check (pull_request) Successful in 3m44s
The app page shows the app's configured branch as a neutral label next
to the status badge, so it can be read without opening the edit page.
The line under the title now shows only the repository. A new test
renders the app page for an app on a non-main branch and checks the
branch is in the title row.

Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
2026-09-29 12:21:35 +02:00
clawbot a836bc5f80 Show the 10 most recent deployments on the deployments page (closes #238)
Check / check (pull_request) Successful in 4m6s
An app's deployments page listed up to 50 deployments; it now lists the
10 most recent, newest first. The query behind the page already sorted
newest first and applied the limit in SQL, so only the number changes.
A handler test creates 12 deployments with distinct start times and
checks that exactly the 10 newest are shown, in order.

Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
2026-09-29 12:02:23 +02:00
clawbot 884abf8512 List every setting upaas reads in the README Configuration table (closes #229)
Check / check (pull_request) Successful in 4m1s
The table left out UPAAS_MAINTENANCE_MODE, UPAAS_SESSION_SECRET and
UPAAS_CORS_ORIGINS, and several rows gave the wrong default or effect.
Each row now matches internal/config/config.go and the code that uses
the value: UPAAS_PORT is also read and wins over PORT, UPAAS_DATA_DIR
must be absolute for deploys unless UPAAS_HOST_DATA_DIR is set,
UPAAS_HOST_DATA_DIR falls back to UPAAS_DATA_DIR and must be absolute
when set, UPAAS_DEBUG also drops the session cookie's Secure flag,
UPAAS_SENTRY_DSN is not used, and /metrics exists only when
UPAAS_METRICS_USERNAME is set. A sentence under the table names the
standard Docker client variables. TODO.md records the step.

Model: opus-5-5
Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
2026-09-29 04:50:31 +02:00
clawbot 2eeead7e64 docs: clarify UPAAS_DATA_DIR default is for local dev only
The ./data default comes from Go code and works for local development.
For Docker deployments, an absolute path should be used.
Updated config table to make this distinction clear.
2026-02-26 02:01:13 -08:00
clawbot 594537e6f5 rework: address review feedback on PR #126
Changes per sneak's review:
- Delete docker-compose.yml, add example stanza to README
- Define custom domain types: ImageID, ContainerID, UnparsedURL
- Use custom types in all function signatures throughout codebase
- Restore imageID parameter (as domain.ImageID) in deploy pipeline
- buildContainerOptions now takes ImageID directly instead of
  constructing image tag from deploymentID
- Fix pre-existing JS formatting (prettier)

make check passes with zero failures.
2026-02-26 02:01:12 -08:00
clawbot a6c76232bf fix: assign commit error to err so deferred rollback triggers (closes #125)
When Commit() failed, the error was stored in commitErr instead of err,
so the deferred rollback (which checks err) was skipped.
2026-02-26 02:00:49 -08:00
clawbot 46574f8cf1 fix: rename GetBuildDir param from appID to appName (closes #123)
The parameter is always called with app.Name, not an ID. Rename to match
actual usage and prevent confusion.
2026-02-26 02:00:49 -08:00
clawbot 074903619d fix: add 1MB size limit on deployment logs with truncation (closes #122)
Cap AppendLog at 1MB, truncating oldest lines when exceeded. Prevents
unbounded SQLite database growth from long-running builds.
2026-02-26 02:00:49 -08:00
clawbot 6cf6e89db4 fix: use renderTemplate in all error paths of HandleAppCreate/HandleAppUpdate (closes #121)
Replace direct tmpl.ExecuteTemplate calls with h.renderTemplate to ensure
buffered rendering and prevent partial HTML responses on template errors.
2026-02-26 02:00:49 -08:00
clawbot 5c20b0b23d fix: use bind mount with HOST_DATA_DIR in docker-compose.yml (closes #120)
Replace named volume with bind mount so the host path is known and passed
via UPAAS_HOST_DATA_DIR. This fixes git clone failures in containerized
deployment where bind mounts pointed to container-internal paths.
2026-02-26 02:00:49 -08:00
clawbot 6d600010b7 ci: add Gitea Actions workflow for make check (closes #96)
Check / check (pull_request) Successful in 11m32s
All external references pinned by commit hash:
- actions/checkout@34e114876b (v4)
- actions/setup-go@40f1582b24 (v5)
- golangci-lint@5d1e709b7b (v2.10.1)
- goimports@009367f5c1 (v0.42.0)
2026-02-20 02:51:10 -08:00
clawbot e9d284698a feat: edit existing env vars, labels, and volume mounts
Add inline edit functionality for environment variables, labels, and
volume mounts on the app detail page. Each entity row now has an Edit
button that reveals an inline form using Alpine.js.

- POST /apps/{id}/env-vars/{varID}/edit
- POST /apps/{id}/labels/{labelID}/edit
- POST /apps/{id}/volumes/{volumeID}/edit
- Path validation for volume host and container paths
- Warning banner about container restart after env var changes
- Tests for ValidateVolumePath

fixes #67
2026-02-16 00:26:07 -08:00
clawbot 3f499163a7 fix: cancel in-progress deploy when webhook triggers new deploy (closes #38)
When a webhook-triggered deploy starts for an app that already has a deploy
in progress, the existing deploy is now cancelled via context cancellation
before the new deploy begins. This prevents silently lost webhook deploys.

Changes:
- Add per-app active deploy tracking with cancel func and done channel
- Deploy() accepts cancelExisting param: true for webhook, false for manual
- Cancelled deployments are marked with new 'cancelled' status
- Add ErrDeployCancelled sentinel error
- Add DeploymentStatusCancelled model constant
- Add comprehensive tests for cancellation mechanics
2026-02-15 22:12:03 -08:00
clawbot cdd7e3fd3a fix: set DestroySession MaxAge to -1 instead of -1*time.Second (closes #39)
The gorilla/sessions MaxAge field expects seconds, not nanoseconds.
Previously MaxAge was set to -1000000000 (-1 * time.Second in nanoseconds),
which worked by accident since any negative value deletes the cookie.
Changed to the conventional value of -1.
2026-02-15 22:07:57 -08:00
clawbot b1a6fd5fca fix: only trust proxy headers from RFC1918/loopback sources (closes #44)
realIP() now parses RemoteAddr and checks if the source IP is in
RFC1918 (10/8, 172.16/12, 192.168/16), loopback (127/8), or IPv6
ULA/loopback ranges before trusting X-Real-IP or X-Forwarded-For
headers. Public source IPs have headers ignored (fail closed).

This prevents attackers from spoofing X-Forwarded-For to bypass
the login rate limiter.
2026-02-15 22:01:54 -08:00
clawbot 763e722607 fix: prevent setup endpoint race condition (closes #26)
Add mutex and INSERT ON CONFLICT to CreateUser to prevent TOCTOU race
where concurrent requests could create multiple admin users.

Changes:
- Add sync.Mutex to auth.Service to serialize CreateUser calls
- Add models.CreateUserAtomic using INSERT ... ON CONFLICT(username) DO NOTHING
- Check RowsAffected to detect conflicts at the DB level (defense-in-depth)
- Add concurrent race condition test (10 goroutines, only 1 succeeds)

The existing UNIQUE constraint on users.username was already in place.
This fix adds the application-level protection (items 1 & 2 from #26).
2026-02-15 21:35:16 -08:00
clawbot 7c0278439d fix: prevent command injection in git clone arguments (closes #18)
- Validate branch names against ^[a-zA-Z0-9._/\-]+$
- Validate commit SHAs against ^[0-9a-f]{40}$
- Pass repo URL, branch, and SHA via environment variables instead of
  interpolating into shell script string
- Add comprehensive tests for validation and injection rejection
2026-02-15 21:33:02 -08:00
clawbot ef0786c4b4 fix: extract real client IP from proxy headers (X-Real-IP / X-Forwarded-For)
Behind a reverse proxy like Traefik, RemoteAddr always contains the
proxy's IP. Add realIP() helper that checks X-Real-IP first, then the
first entry of X-Forwarded-For, falling back to RemoteAddr.

Update both LoginRateLimit and Logging middleware to use realIP().
Add comprehensive tests for the new function.

Fixes #12
2026-02-15 21:14:12 -08:00
clawbot 867cdf01ab fix: add ownership verification on env var, label, volume, and port deletion
Verify that the resource's AppID matches the URL path app ID before
allowing deletion. Without this check, any authenticated user could
delete resources belonging to any app by providing the target resource's
ID in the URL regardless of the app ID in the path (IDOR vulnerability).

Closes #19
2026-02-15 21:02:46 -08:00
clawbot 6475389280 test: add IDOR tests for resource deletion ownership verification
Tests demonstrate that env vars, labels, volumes, and ports can be
deleted via another app's URL path without ownership checks.

All 4 tests fail, confirming the vulnerability described in #19.
2026-02-15 21:00:41 -08:00