A workspace proxy hostname prefix could be accepted as a valid proxy
access URL. An authenticated user could then be redirected to an
attacker-controlled domain with an application-connect API key in the
URL.
Require proxy access URL matches to have a hostname boundary after the
candidate hostname, allowing only the end of the URL, a port, or a
path.
Add regression coverage for proxy access URL and wildcard hostname
prefixes.
Refs: https://linear.app/codercom/issue/PLAT-384
---------
Co-authored-by: Bobby Ho <bobbidinho@gmail.com>
Under CI load the request's 10ms revoke timeout could expire before
the request reached the FakeIDP revoke handler. The handler never
ran, so the test's wait for it to finish blocked until the 25s test
context expired instead of passing quickly.
Raise `RevokeTimeout` to 100ms so the request has ~10x more headroom to
reach the handler under load. After RevokeToken returns, check a
`handlerStarted` signal before asserting: this anchors the
`DeadlineExceeded` assertion to a request that was actually in flight,
and turns any residual scheduling race into a fast, labeled failure
instead of a hang.
Unblock the handler on the early-exit path with a `t.Cleanup`. It must
be registered after the FakeIDP setup so LIFO runs it before the
server's `Close()`; otherwise a handler that dispatched late would
block `Close()` and hang teardown until the test timeout. Drop the
previous `time.Sleep` watchdog and the handler-done channel, since the
FakeIDP server's `Close()` already joins the in-flight handler.
Refs: https://linear.app/codercom/issue/PLAT-317
This models restart as durable orchestration of existing stop and
start workspace builds instead of adding a new restart transition.
Keeping restart as two existing transitions preserves the current
build/provisioner model.
The child start build is created only after the parent stop build
succeeds, rather than being inserted immediately in a pending
state. That keeps `workspace_builds` aligned with actual
provisioner-ready work and avoids introducing a second
pending-build lifecycle that the provisioner and build acquisition
paths would need to understand.
Refs: https://linear.app/codercom/issue/PLAT-143
User Admin password resets could update the target user's hashed
password but fail while revoking that user's API keys. The transaction
then rolled back and returned HTTP 500, so the password was never
changed.
Add a user-scoped API key revoker actor and use it in both password
reset flows so key revocation succeeds without broader system auth.
Refs: https://linear.app/codercom/issue/PLAT-316
Follow-up to #26584.
After the nixpkgs 25.05 update, the default go-migrate package panics at
startup due to its Snowflake driver before Postgres commands can run.
Coder only uses the migrate CLI for migration creation and local Postgres
migrations, so override the build tags to include only the Postgres driver.
`nix develop` and `nix-shell` were broken because the `nixos-24.11`
`google-chrome` derivation still pointed at Chrome 138, and Google no
longer serves that versioned `.deb`.
Update the flake's main `nixpkgs` input to `nixos-25.05` so the
shell resolves a current Chrome package again. Pin `protoc
23.4` explicitly from the upstream protobuf release archives. This keeps
the local Nix shell aligned with `mise.toml` and the CI/release codegen
toolchain.
`TestInvalidateTemplatePrebuilds` assumed a stable ordering from preset
invalidation results, but the underlying `UPDATE ... RETURNING` query
does not guarantee row order. Local WIP schema changes were enough to
surface that latent flake when running tests.
Update the test to compare invalidated presets as a set instead of by
slice position. This keeps the behavior under test the same, while
removing dependence on unspecified database row ordering.
Subdomain app routing derived the app identity from
httpapi.RequestHost, which returned the client-supplied
X-Forwarded-Host header verbatim. No middleware validated or stripped
that header, so a request from an untrusted peer could forge it. Since
the application_connect cookie is scoped to the wildcard apps domain,
JavaScript in a share=authenticated app could fetch() with a forged
X-Forwarded-Host pointing at a victim's owner-only app; coderd routed
and authorized the request as the victim and returned the private app
response same-origin to the attacker.
Replace RequestHost with httpmw.EffectiveHost, which honors
X-Forwarded-Host only when the original socket peer is a configured
trusted origin, otherwise falling back to the received Host header.
This ties host trust to the same RealIPConfig model already used for
X-Forwarded-For and -Proto. Wire it into HandleSubdomain for both
coderd and wsproxy, and log both the effective host and the raw
received_host.
Add coverage: EffectiveHost unit tests assert the trust decision uses
the socket peer rather than the spoofable forwarded client IP, and a
HandleSubdomain test confirms a forged X-Forwarded-Host from an
untrusted peer never reaches token resolution.
Refs: https://linear.app/codercom/issue/PLAT-259
fix(coderd/workspaceapps): verify workspace owner matches app username
When resolving a workspace app by workspace UUID, the URL's username
segment was never reconciled against the resolved workspace's owner.
A user could serve their own workspace app from a hostname embedding
another user's username, so the parsed origin username belonged to the
victim. Combined with the username-equality CORS check, this allowed
credentialed cross-origin reads of the victim's app responses.
Reject the request with a 404 when the resolved workspace's owner does
not match the user named in the request.
Refs: https://linear.app/codercom/issue/PLAT-260
Enforce aggregate limits when converting uploaded ZIP archives to tar
so compressed inputs cannot expand without bound in memory.
Also treat malformed ZIP entry metadata and content mismatches as
client errors during conversion, returning 400 for invalid archives and
413 when expanded tar output exceeds the upload limit.
Ref: https://linear.app/codercom/issue/PLAT-274/zip-upload-decompressed-without-aggregate-size-limit-sec-103
The nix dev shell was forcing BIOME_BINARY to the musl package, but
pnpm install on glibc hosts only installs the glibc biome package by
default. That caused pnpm exec biome to fail in nix-shell even though
the default biome binary worked when BIOME_BINARY was unset.
feat(provisioner/terraform): log resource replacement details
Log compact Terraform resource replacement warnings for non-prebuild claim builds.
The warnings include Terraform-reported replacement paths and before/after values
when Terraform does not mark the value, or a descendant of the value, sensitive.
Preserve Terraform sensitivity and unknown handling, and fall back to path-only or
pathless messages when value details are unavailable. Prebuild claims continue
using the existing full drift log path, including for pathless replacement actions
that were previously skipped.
Continue sending PlanComplete.ResourceReplacements only when Terraform reports
replacement paths.
Ref:
https://linear.app/codercom/issue/PLAT-135/bug-build-terraform-logs-dont-include-which-fields-have-changed
Ref: https://github.com/coder/coder/issues/16999
Azure IMDS attested data signatures can now chain through
Microsoft TLS G2 RSA CA OCSP intermediates, then through the
cross-signed Microsoft TLS RSA Root G2 certificate, before reaching
DigiCert Global Root G2.
coderd did not bundle the new G2 OCSP intermediates or the
cross-signed Microsoft TLS RSA Root G2 bridge certificate, so it could
fail to build a trusted chain for affected IMDS signatures.
Related to:
https://linear.app/codercom/issue/PLAT-205/bug-azure-instance-identity-verification-is-broken
Previously, `runTx` could lose a deferred rollback failure when returning an
existing transaction error, because the rollback path could not update the final
return value.
https://go.dev/play/p/AhBK31lO0Gd
Previously, the workspace "Download logs" dialog formatted the original byte
count with the promoted unit, so sizes above 1 KiB could be shown incorrectly,
for example `4472 KB` instead of `4.37 KB`. Exact 1024-byte files also stayed
in bytes.
Audit and connection log pages were timing out due to expensive COUNT(*)
queries over large tables. This commit adds opt-in count capping: requests can
return a `count_cap` field signaling that the count was truncated at a threshold,
avoiding full table scans that caused page timeouts.
Text-cast UUID comparisons in regosql-generated authorization queries
also contributed to the slowdown by preventing index usage for connection
and audit log queries. These now emit native UUID operators.
Frontend changes handle the capped state in usePaginatedQuery and
PaginationWidget, optionally displaying a capped count in the pagination
UI (e.g. "Showing 2,076 to 2,100 of 2,000+ logs")
Related to:
https://linear.app/codercom/issue/PLAT-31/connectionaudit-log-performance-issue
Introduce a three-way workspace sharing setting (none, everyone,
service_accounts) replacing the boolean workspace_sharing_disabled.
In service_accounts mode, only service account-owned workspaces can be
shared while regular members' share permissions are removed. Adds a
new organization-service-account system role with per-org permissions
reconciled alongside the existing organization-member system role.
Related to:
https://linear.app/codercom/issue/PLAT-28/feat-service-accounts-sharing-mode-and-rbac-role
---------
Co-authored-by: Steven Masley <Emyrk@users.noreply.github.com>
Co-authored-by: Kayla はな <mckayla@hey.com>
The `--parameter-default` value is now used to pre-select the default option for a coder parameter
with option blocks when prompting interactively in CLI.
Related to: https://github.com/coder/coder/issues/22078
Justification:
- Populating `members` is authorized with `group_member.read` which is
not required to be able to share a workspace
- Populating `total_member_count` is authorized with `group.read` which
is required to be able to share
- The updated helper is only used in template/workspace sharing UIs, so
other pages that might need counts of readable members are unaffected
Related to: https://github.com/coder/internal/issues/1302
Use transition-specific actions when authorizing workspace build
parameter inserts in the database layer so start/stop/delete do not
require workspace.update.
Related to: https://github.com/coder/internal/issues/1299
The removal of that permission from the role broke valid use cases (e.g.
a site owner user creating a workspace owned by a system account and
then trying to share it with another user).
The bulk of the PR is made up of the rollbacks of the previously
introduced test updates necessitated by the removal.
Related to: https://github.com/coder/internal/issues/1285
Adds a per-organization setting to disable workspace sharing. When enabled,
all existing workspace ACLs in the organization are cleared and the workspace
ACL mutation API endpoints return `403 Forbidden`.
This complements the existing site-wide `--disable-workspace-sharing` flag by
providing more granular control at the organization level.
Closes https://github.com/coder/internal/issues/1073 (part 2)
---------
Co-authored-by: Steven Masley <Emyrk@users.noreply.github.com>
It appears on newer Debian systems `Canada/Newfoundland` TZ is not
present and `America/St_Johns` should be used instead. Coder tests use a
docker PG image where `Canada/Newfoundland` is still supported:
```
$ docker run --rm -it us-docker.pkg.dev/coder-v2-images-public/public/postgres:17 bash
root@ca99e82721dc:/# ls -l /usr/share/zoneinfo/Canada/Newfoundland
lrwxrwxrwx 1 root root 19 Mar 26 2025 /usr/share/zoneinfo/Canada/Newfoundland -> ../America/St_Johns
```
However, if a local PG instance is running on a Debian Trixie host,
coder test will use it and error out due to the zone being unavailable:
```
$ docker run --rm -it debian:trixie bash
root@f285092767e4:/# ls -l /usr/share/zoneinfo/Canada/Newfoundland
ls: cannot access '/usr/share/zoneinfo/Canada/Newfoundland': No such file or directory
root@f285092767e4:/# ls -l /usr/share/zoneinfo/America/St_Johns
-rw-r--r-- 1 root root 3655 Aug 24 20:12 /usr/share/zoneinfo/America/St_Johns
```
... which causes the tests to error out:
```
$ go test ./enterprise/coderd
--- FAIL: TestWorkspaceTemplateParamsChange (0.13s)
workspaces_test.go:3097: TestWorkspaceTagsTerraform: using cached terraform providers
workspaces_test.go:3097: Set TF_CLI_CONFIG_FILE=/home/geo/.cache/coderv2-test/terraform_workspace_tags_test/a28ed341dee8/terraform.rc
coderdenttest.go:84:
Error Trace: /home/geo/coder/coderd/database/dbtestutil/db.go:161
/home/geo/coder/coderd/database/dbtestutil/db.go:122
/home/geo/coder/coderd/coderdtest/coderdtest.go:270
/home/geo/coder/enterprise/coderd/coderdenttest/coderdenttest.go:105
/home/geo/coder/enterprise/coderd/coderdenttest/coderdenttest.go:84
/home/geo/coder/enterprise/coderd/coderdenttest/coderdenttest.go:84
/home/geo/coder/enterprise/coderd/workspaces_test.go:3103
Error: Received unexpected error:
pq: invalid value for parameter "TimeZone": "Canada/Newfoundland"
Test: TestWorkspaceTemplateParamsChange
Messages: failed to set timezone for database
...
```
This commit replaces the problematic TZ with the canonical one.
Adds `--disable-workspace-sharing` option.
Workspace sharing is disabled by not including user and group ACLs in
the workspace RBAC object, which prevents ACL-based authz.
Closes https://github.com/coder/internal/issues/1072
The commit also adds saving of workspace user/group ACLs in the test DB
data generator.