Skip to content

Commit 279f5fe

Browse files
authored
Merge branch 'main' into fix-postgres-url-docs
2 parents 5a9de84 + 5f50dcc commit 279f5fe

File tree

172 files changed

+4424
-1581
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

172 files changed

+4424
-1581
lines changed

.claude/scripts/format.sh

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,87 @@
66

77
set -euo pipefail
88

9+
# A variable to memoize the command for canonicalizing paths.
10+
_CANONICALIZE_CMD=""
11+
12+
# canonicalize_path resolves a path to its absolute, canonical form.
13+
# It tries 'realpath' and 'readlink -f' in order.
14+
# The chosen command is memoized to avoid repeated checks.
15+
# If none of these are available, it returns an empty string.
16+
canonicalize_path() {
17+
local path_to_resolve="$1"
18+
19+
# If we haven't determined a command yet, find one.
20+
if [[ -z "$_CANONICALIZE_CMD" ]]; then
21+
if command -v realpath >/dev/null 2>&1; then
22+
_CANONICALIZE_CMD="realpath"
23+
elif command -v readlink >/dev/null 2>&1 && readlink -f . >/dev/null 2>&1; then
24+
_CANONICALIZE_CMD="readlink"
25+
else
26+
# No command found, so we can't resolve.
27+
# We set a "none" value to prevent re-checking.
28+
_CANONICALIZE_CMD="none"
29+
fi
30+
fi
31+
32+
# Now, execute the command.
33+
case "$_CANONICALIZE_CMD" in
34+
realpath)
35+
realpath "$path_to_resolve" 2>/dev/null
36+
;;
37+
readlink)
38+
readlink -f "$path_to_resolve" 2>/dev/null
39+
;;
40+
*)
41+
# This handles the "none" case or any unexpected error.
42+
echo ""
43+
;;
44+
esac
45+
}
46+
947
# Read JSON input from stdin
1048
input=$(cat)
1149

1250
# Extract the file path from the JSON input
1351
# Expected format: {"tool_input": {"file_path": "/absolute/path/to/file"}} or {"tool_response": {"filePath": "/absolute/path/to/file"}}
1452
file_path=$(echo "$input" | jq -r '.tool_input.file_path // .tool_response.filePath // empty')
1553

54+
# Secure path canonicalization to prevent path traversal attacks
55+
# Resolve repo root to an absolute, canonical path.
56+
repo_root_raw="$(cd "$(dirname "$0")/../.." && pwd)"
57+
repo_root="$(canonicalize_path "$repo_root_raw")"
58+
if [[ -z "$repo_root" ]]; then
59+
# Fallback if canonicalization fails
60+
repo_root="$repo_root_raw"
61+
fi
62+
63+
# Resolve the input path to an absolute path
64+
if [[ "$file_path" = /* ]]; then
65+
# Already absolute
66+
abs_file_path="$file_path"
67+
else
68+
# Make relative paths absolute from repo root
69+
abs_file_path="$repo_root/$file_path"
70+
fi
71+
72+
# Canonicalize the path (resolve symlinks and ".." segments)
73+
canonical_file_path="$(canonicalize_path "$abs_file_path")"
74+
75+
# Check if canonicalization failed or if the resolved path is outside the repo
76+
if [[ -z "$canonical_file_path" ]] || { [[ "$canonical_file_path" != "$repo_root" ]] && [[ "$canonical_file_path" != "$repo_root"/* ]]; }; then
77+
echo "Error: File path is outside repository or invalid: $file_path" >&2
78+
exit 1
79+
fi
80+
81+
# Handle the case where the file path is the repository root itself.
82+
if [[ "$canonical_file_path" == "$repo_root" ]]; then
83+
echo "Warning: Formatting the repository root is not a supported operation. Skipping." >&2
84+
exit 0
85+
fi
86+
87+
# Convert back to relative path from repo root for consistency
88+
file_path="${canonical_file_path#"$repo_root"/}"
89+
1690
if [[ -z "$file_path" ]]; then
1791
echo "Error: No file path provided in input" >&2
1892
exit 1

.github/workflows/ci.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ jobs:
187187
188188
# Check for any typos
189189
- name: Check for typos
190-
uses: crate-ci/typos@b1ae8d918b6e85bd611117d3d9a3be4f903ee5e4 # v1.33.1
190+
uses: crate-ci/typos@392b78fe18a52790c53f42456e46124f77346842 # v1.34.0
191191
with:
192192
config: .github/workflows/typos.toml
193193

@@ -921,7 +921,7 @@ jobs:
921921
# the check to pass. This is desired in PRs, but not in mainline.
922922
- name: Publish to Chromatic (non-mainline)
923923
if: github.ref != 'refs/heads/main' && github.repository_owner == 'coder'
924-
uses: chromaui/action@b5848056bb67ce5f1cccca8e62a37cbd9dd42871 # v13.0.1
924+
uses: chromaui/action@4d8ebd13658d795114f8051e25c28d66f14886c6 # v13.1.2
925925
env:
926926
NODE_OPTIONS: "--max_old_space_size=4096"
927927
STORYBOOK: true
@@ -953,7 +953,7 @@ jobs:
953953
# infinitely "in progress" in mainline unless we re-review each build.
954954
- name: Publish to Chromatic (mainline)
955955
if: github.ref == 'refs/heads/main' && github.repository_owner == 'coder'
956-
uses: chromaui/action@b5848056bb67ce5f1cccca8e62a37cbd9dd42871 # v13.0.1
956+
uses: chromaui/action@4d8ebd13658d795114f8051e25c28d66f14886c6 # v13.1.2
957957
env:
958958
NODE_OPTIONS: "--max_old_space_size=4096"
959959
STORYBOOK: true

.github/workflows/docs-ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
- name: Setup Node
2929
uses: ./.github/actions/setup-node
3030

31-
- uses: tj-actions/changed-files@666c9d29007687c52e3c7aa2aac6c0ffcadeadc3 # v45.0.7
31+
- uses: tj-actions/changed-files@cf79a64fed8a943fb1073260883d08fe0dfb4e56 # v45.0.7
3232
id: changed-files
3333
with:
3434
files: |

.github/workflows/scorecard.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,6 @@ jobs:
4747

4848
# Upload the results to GitHub's code scanning dashboard.
4949
- name: "Upload to code-scanning"
50-
uses: github/codeql-action/upload-sarif@39edc492dbe16b1465b0cafca41432d857bdb31a # v3.29.1
50+
uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
5151
with:
5252
sarif_file: results.sarif

.github/workflows/security.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ jobs:
3838
uses: ./.github/actions/setup-go
3939

4040
- name: Initialize CodeQL
41-
uses: github/codeql-action/init@39edc492dbe16b1465b0cafca41432d857bdb31a # v3.29.1
41+
uses: github/codeql-action/init@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
4242
with:
4343
languages: go, javascript
4444

@@ -48,7 +48,7 @@ jobs:
4848
rm Makefile
4949
5050
- name: Perform CodeQL Analysis
51-
uses: github/codeql-action/analyze@39edc492dbe16b1465b0cafca41432d857bdb31a # v3.29.1
51+
uses: github/codeql-action/analyze@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
5252

5353
- name: Send Slack notification on failure
5454
if: ${{ failure() }}
@@ -142,15 +142,15 @@ jobs:
142142
echo "image=$(cat "$image_job")" >> $GITHUB_OUTPUT
143143
144144
- name: Run Trivy vulnerability scanner
145-
uses: aquasecurity/trivy-action@76071ef0d7ec797419534a183b498b4d6366cf37
145+
uses: aquasecurity/trivy-action@dc5a429b52fcf669ce959baa2c2dd26090d2a6c4
146146
with:
147147
image-ref: ${{ steps.build.outputs.image }}
148148
format: sarif
149149
output: trivy-results.sarif
150150
severity: "CRITICAL,HIGH"
151151

152152
- name: Upload Trivy scan results to GitHub Security tab
153-
uses: github/codeql-action/upload-sarif@39edc492dbe16b1465b0cafca41432d857bdb31a # v3.29.1
153+
uses: github/codeql-action/upload-sarif@181d5eefc20863364f96762470ba6f862bdef56b # v3.29.2
154154
with:
155155
sarif_file: trivy-results.sarif
156156
category: "Trivy"

.github/workflows/weekly-docs.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
3030

3131
- name: Check Markdown links
32-
uses: umbrelladocs/action-linkspector@e2ccef58c4b9eb89cd71ee23a8629744bba75aa6 # v1.3.5
32+
uses: umbrelladocs/action-linkspector@3a951c1f0dca72300c2320d0eb39c2bafe429ab1 # v1.3.6
3333
id: markdown-link-check
3434
# checks all markdown files from /docs including all subfolders
3535
with:

.mcp.json

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,36 @@
11
{
2-
"mcpServers": {
3-
"go-language-server": {
4-
"type": "stdio",
5-
"command": "go",
6-
"args": [
7-
"run",
8-
"github.com/isaacphi/mcp-language-server@latest",
9-
"-workspace",
10-
"./",
11-
"-lsp",
12-
"go",
13-
"--",
14-
"run",
15-
"golang.org/x/tools/gopls@latest"
16-
],
17-
"env": {}
18-
},
19-
"typescript-language-server": {
20-
"type": "stdio",
21-
"command": "go",
22-
"args": [
23-
"run",
24-
"github.com/isaacphi/mcp-language-server@latest",
25-
"-workspace",
26-
"./site/",
27-
"-lsp",
28-
"pnpx",
29-
"--",
30-
"typescript-language-server",
31-
"--stdio"
32-
],
33-
"env": {}
34-
}
35-
}
36-
}
2+
"mcpServers": {
3+
"go-language-server": {
4+
"type": "stdio",
5+
"command": "go",
6+
"args": [
7+
"run",
8+
"github.com/isaacphi/mcp-language-server@latest",
9+
"-workspace",
10+
"./",
11+
"-lsp",
12+
"go",
13+
"--",
14+
"run",
15+
"golang.org/x/tools/gopls@latest"
16+
],
17+
"env": {}
18+
},
19+
"typescript-language-server": {
20+
"type": "stdio",
21+
"command": "go",
22+
"args": [
23+
"run",
24+
"github.com/isaacphi/mcp-language-server@latest",
25+
"-workspace",
26+
"./site/",
27+
"-lsp",
28+
"pnpx",
29+
"--",
30+
"typescript-language-server",
31+
"--stdio"
32+
],
33+
"env": {}
34+
}
35+
}
36+
}

agent/agent.go

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -336,18 +336,16 @@ func (a *agent) init() {
336336
// will not report anywhere.
337337
a.scriptRunner.RegisterMetrics(a.prometheusRegistry)
338338

339-
if a.devcontainers {
340-
containerAPIOpts := []agentcontainers.Option{
341-
agentcontainers.WithExecer(a.execer),
342-
agentcontainers.WithCommandEnv(a.sshServer.CommandEnv),
343-
agentcontainers.WithScriptLogger(func(logSourceID uuid.UUID) agentcontainers.ScriptLogger {
344-
return a.logSender.GetScriptLogger(logSourceID)
345-
}),
346-
}
347-
containerAPIOpts = append(containerAPIOpts, a.containerAPIOptions...)
348-
349-
a.containerAPI = agentcontainers.NewAPI(a.logger.Named("containers"), containerAPIOpts...)
339+
containerAPIOpts := []agentcontainers.Option{
340+
agentcontainers.WithExecer(a.execer),
341+
agentcontainers.WithCommandEnv(a.sshServer.CommandEnv),
342+
agentcontainers.WithScriptLogger(func(logSourceID uuid.UUID) agentcontainers.ScriptLogger {
343+
return a.logSender.GetScriptLogger(logSourceID)
344+
}),
350345
}
346+
containerAPIOpts = append(containerAPIOpts, a.containerAPIOptions...)
347+
348+
a.containerAPI = agentcontainers.NewAPI(a.logger.Named("containers"), containerAPIOpts...)
351349

352350
a.reconnectingPTYServer = reconnectingpty.NewServer(
353351
a.logger.Named("reconnecting-pty"),
@@ -1162,7 +1160,7 @@ func (a *agent) handleManifest(manifestOK *checkpoint) func(ctx context.Context,
11621160
scripts = manifest.Scripts
11631161
devcontainerScripts map[uuid.UUID]codersdk.WorkspaceAgentScript
11641162
)
1165-
if a.containerAPI != nil {
1163+
if a.devcontainers {
11661164
// Init the container API with the manifest and client so that
11671165
// we can start accepting requests. The final start of the API
11681166
// happens after the startup scripts have been executed to
@@ -1197,7 +1195,7 @@ func (a *agent) handleManifest(manifestOK *checkpoint) func(ctx context.Context,
11971195
// autostarted devcontainer will be included in this time.
11981196
err := a.scriptRunner.Execute(a.gracefulCtx, agentscripts.ExecuteStartScripts)
11991197

1200-
if a.containerAPI != nil {
1198+
if a.devcontainers {
12011199
// Start the container API after the startup scripts have
12021200
// been executed to ensure that the required tools can be
12031201
// installed.
@@ -1928,10 +1926,8 @@ func (a *agent) Close() error {
19281926
a.logger.Error(a.hardCtx, "script runner close", slog.Error(err))
19291927
}
19301928

1931-
if a.containerAPI != nil {
1932-
if err := a.containerAPI.Close(); err != nil {
1933-
a.logger.Error(a.hardCtx, "container API close", slog.Error(err))
1934-
}
1929+
if err := a.containerAPI.Close(); err != nil {
1930+
a.logger.Error(a.hardCtx, "container API close", slog.Error(err))
19351931
}
19361932

19371933
// Wait for the graceful shutdown to complete, but don't wait forever so

agent/agent_test.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2441,7 +2441,8 @@ func TestAgent_DevcontainersDisabledForSubAgent(t *testing.T) {
24412441

24422442
// Setup the agent with devcontainers enabled initially.
24432443
//nolint:dogsled
2444-
conn, _, _, _, _ := setupAgent(t, manifest, 0, func(*agenttest.Client, *agent.Options) {
2444+
conn, _, _, _, _ := setupAgent(t, manifest, 0, func(_ *agenttest.Client, o *agent.Options) {
2445+
o.Devcontainers = true
24452446
})
24462447

24472448
// Query the containers API endpoint. This should fail because
@@ -2453,8 +2454,8 @@ func TestAgent_DevcontainersDisabledForSubAgent(t *testing.T) {
24532454
require.Error(t, err)
24542455

24552456
// Verify the error message contains the expected text.
2456-
require.Contains(t, err.Error(), "The agent dev containers feature is experimental and not enabled by default.")
2457-
require.Contains(t, err.Error(), "To enable this feature, set CODER_AGENT_DEVCONTAINERS_ENABLE=true in your template.")
2457+
require.Contains(t, err.Error(), "Dev Container feature not supported.")
2458+
require.Contains(t, err.Error(), "Dev Container integration inside other Dev Containers is explicitly not supported.")
24582459
}
24592460

24602461
func TestAgent_Dial(t *testing.T) {

agent/api.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"time"
77

88
"github.com/go-chi/chi/v5"
9+
"github.com/google/uuid"
910

1011
"github.com/coder/coder/v2/coderd/httpapi"
1112
"github.com/coder/coder/v2/codersdk"
@@ -36,12 +37,19 @@ func (a *agent) apiHandler() http.Handler {
3637
cacheDuration: cacheDuration,
3738
}
3839

39-
if a.containerAPI != nil {
40+
if a.devcontainers {
4041
r.Mount("/api/v0/containers", a.containerAPI.Routes())
42+
} else if manifest := a.manifest.Load(); manifest != nil && manifest.ParentID != uuid.Nil {
43+
r.HandleFunc("/api/v0/containers", func(w http.ResponseWriter, r *http.Request) {
44+
httpapi.Write(r.Context(), w, http.StatusForbidden, codersdk.Response{
45+
Message: "Dev Container feature not supported.",
46+
Detail: "Dev Container integration inside other Dev Containers is explicitly not supported.",
47+
})
48+
})
4149
} else {
4250
r.HandleFunc("/api/v0/containers", func(w http.ResponseWriter, r *http.Request) {
4351
httpapi.Write(r.Context(), w, http.StatusForbidden, codersdk.Response{
44-
Message: "The agent dev containers feature is experimental and not enabled by default.",
52+
Message: "Dev Container feature not enabled.",
4553
Detail: "To enable this feature, set CODER_AGENT_DEVCONTAINERS_ENABLE=true in your template.",
4654
})
4755
})

0 commit comments

Comments
 (0)