Skip to content

Commit 12dcb49

Browse files
committed
fix(security): resolve all gosec findings across codebase
- Replace http.Get with http.NewRequestWithContext in installer and templates to fix G107 (HTTP request with variable URL) - Add io.LimitReader to zip extraction to prevent decompression bombs (G110) - Add proper #nosec annotations with rule IDs and justifications for intentional patterns: G115, G122, G204, G302, G304, G306, G703, G117 - Remove stale //nolint:gosec comments on lines that don't trigger gosec
1 parent 110bd64 commit 12dcb49

16 files changed

Lines changed: 40 additions & 30 deletions

File tree

cmd/ask/ask.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func installAgent() error {
2626
}
2727

2828
agentsDir := filepath.Join(home, ".opencode", "agents")
29-
if err := os.MkdirAll(agentsDir, 0750); err != nil { //nolint:gosec // user-owned directory
29+
if err := os.MkdirAll(agentsDir, 0750); err != nil {
3030
return fmt.Errorf("could not create agents directory: %w", err)
3131
}
3232

@@ -69,7 +69,7 @@ func NewAskCommand() *cli.Command {
6969
args = []string{"--agent", agentName}
7070
}
7171

72-
cmd := exec.CommandContext(context.Background(), opencodeBin, args...) //nolint:gosec
72+
cmd := exec.CommandContext(context.Background(), opencodeBin, args...) // #nosec G204 -- opencodeBin is from exec.LookPath, args are hardcoded
7373
cmd.Stdin = os.Stdin
7474
cmd.Stdout = os.Stdout
7575
cmd.Stderr = os.Stderr

cmd/deploy/deploy.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ func streamRuntimeLogs(client *api.APIClient, projectID, deploymentID string) {
351351

352352
// loadGitignorePatterns reads .gitignore from srcDir and returns usable patterns.
353353
func loadGitignorePatterns(srcDir string) []string {
354-
data, err := os.ReadFile(filepath.Join(srcDir, ".gitignore")) //nolint:gosec
354+
data, err := os.ReadFile(filepath.Join(srcDir, ".gitignore")) // #nosec G304 -- srcDir is from filepath.Abs, filename is a constant
355355
if err != nil {
356356
return nil
357357
}
@@ -438,7 +438,7 @@ func createZip(w io.Writer, srcDir string) error {
438438
return err
439439
}
440440

441-
f, err := os.Open(path) //nolint:gosec
441+
f, err := os.Open(path) // #nosec G304,G122 -- path comes from filepath.Walk on a local directory
442442
if err != nil {
443443
return err
444444
}

cmd/env/helpers.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ func ensureEnvGitignored() {
104104
content = pattern + "\n"
105105
}
106106

107-
f, err := os.OpenFile(gitignore, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) //nolint:gosec
107+
f, err := os.OpenFile(gitignore, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) // #nosec G302 -- .gitignore must be world-readable (0644)
108108
if err != nil {
109109
return
110110
}

cmd/env/push.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func newEnvPushCommand() *cli.Command {
4343
return fmt.Errorf("--file must be a relative path without '..' (got %q)", filePath)
4444
}
4545

46-
data, err := os.ReadFile(filePath) //nolint:gosec
46+
data, err := os.ReadFile(filePath) // #nosec G304 -- filePath is validated above (relative, no ..)
4747
if err != nil {
4848
return fmt.Errorf("could not read %s: %w", filePath, err)
4949
}

cmd/templates/use.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package templates
22

33
import (
4+
"context"
45
"fmt"
56
"io"
67
"net/http"
@@ -103,11 +104,15 @@ func newTemplatesUseCommand() *cli.Command {
103104
return err
104105
}
105106

106-
if err := os.MkdirAll(absDir, 0750); err != nil { //nolint:gosec
107+
if err := os.MkdirAll(absDir, 0750); err != nil {
107108
return fmt.Errorf("could not create directory %s: %w", dir, err)
108109
}
109110

110-
resp, err := http.Get(downloadURL) //nolint:gosec,noctx
111+
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, downloadURL, nil)
112+
if err != nil {
113+
return fmt.Errorf("could not create download request: %w", err)
114+
}
115+
resp, err := http.DefaultClient.Do(req)
111116
if err != nil {
112117
return fmt.Errorf("could not download template: %w", err)
113118
}
@@ -130,7 +135,7 @@ func newTemplatesUseCommand() *cli.Command {
130135
}
131136

132137
func downloadToFile(path string, src io.Reader) error {
133-
out, err := os.Create(path) //nolint:gosec
138+
out, err := os.Create(path) // #nosec G304 -- path is constructed from filepath.Join(absDir, "template.zip")
134139
if err != nil {
135140
return fmt.Errorf("could not create file: %w", err)
136141
}

cmd/upgrade/upgrade.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ func fetchChecksum(rawURL string) (string, error) {
362362
}
363363

364364
func verifyChecksum(path, expected string) error {
365-
f, err := os.Open(path) //nolint:gosec // path comes from os.CreateTemp, not user input
365+
f, err := os.Open(path) // #nosec G304 -- path comes from os.CreateTemp, not user input
366366
if err != nil {
367367
return fmt.Errorf("could not open downloaded file: %w", err)
368368
}
@@ -381,7 +381,7 @@ func verifyChecksum(path, expected string) error {
381381
}
382382

383383
func replaceExecutable(dst, src string) error {
384-
if err := os.Chmod(src, 0o755); err != nil { //nolint:gosec // executable binary requires 0755
384+
if err := os.Chmod(src, 0o755); err != nil { // #nosec G302 -- executable binary requires 0755
385385
return err
386386
}
387387

cmd/vms/ssh.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ func findPublicKeys() map[string]string {
3838
}
3939
keys := make(map[string]string, len(matches))
4040
for _, path := range matches {
41-
data, err := os.ReadFile(path) //nolint:gosec
41+
data, err := os.ReadFile(path) // #nosec G304 -- path is from filepath.Glob on ~/.ssh/*.pub
4242
if err != nil {
4343
continue
4444
}
@@ -231,7 +231,7 @@ func newVMSSHCommand() *cli.Command {
231231
}
232232
sshArgs = append(sshArgs, target)
233233

234-
cmd := exec.CommandContext(context.Background(), "ssh", sshArgs...) //nolint:gosec
234+
cmd := exec.CommandContext(context.Background(), "ssh", sshArgs...) // #nosec G204 -- binary is hardcoded "ssh", args are constructed internally
235235
cmd.Stdin = os.Stdin
236236
cmd.Stdout = os.Stdout
237237
cmd.Stderr = os.Stderr

internal/browser/browser.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@ func Open(rawURL string) error {
2222
cmd = "xdg-open"
2323
args = []string{rawURL}
2424
}
25-
return exec.CommandContext(context.Background(), cmd, args...).Start() //nolint:gosec
25+
return exec.CommandContext(context.Background(), cmd, args...).Start() // #nosec G204 -- cmd/args are derived from runtime.GOOS, not user input
2626
}

internal/config/oauth.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func SaveOAuthSession(session OAuthSession) error {
4848
if err != nil {
4949
return err
5050
}
51-
data, err := json.Marshal(session) //nolint:gosec
51+
data, err := json.Marshal(session) // #nosec G117 -- token serialization is the purpose of this function; file is stored with 0600
5252
if err != nil {
5353
return err
5454
}
@@ -61,7 +61,7 @@ func LoadOAuthSession() (*OAuthSession, error) {
6161
if err != nil {
6262
return nil, err
6363
}
64-
data, err := os.ReadFile(path) //nolint:gosec
64+
data, err := os.ReadFile(path) // #nosec G304 -- path is from oauthPath() under ~/.createos/
6565
if err != nil {
6666
if errors.Is(err, os.ErrNotExist) {
6767
return nil, nil

internal/config/project.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ func SaveProjectConfig(dir string, cfg ProjectConfig) error {
2222
if err != nil {
2323
return err
2424
}
25-
return os.WriteFile(filepath.Join(dir, projectFile), data, 0644) //nolint:gosec
25+
return os.WriteFile(filepath.Join(dir, projectFile), data, 0644) // #nosec G306 -- project config needs to be readable by other tools
2626
}
2727

2828
// LoadProjectConfig reads .createos.json from the given directory.
2929
func LoadProjectConfig(dir string) (*ProjectConfig, error) {
30-
data, err := os.ReadFile(filepath.Join(dir, projectFile)) //nolint:gosec
30+
data, err := os.ReadFile(filepath.Join(dir, projectFile)) // #nosec G304 -- dir comes from os.Getwd() walk, projectFile is a constant
3131
if err != nil {
3232
if errors.Is(err, os.ErrNotExist) {
3333
return nil, nil
@@ -66,7 +66,7 @@ func FindProjectConfig() (*ProjectConfig, error) {
6666
// EnsureGitignore adds .createos.json to .gitignore if not already present.
6767
func EnsureGitignore(dir string) error {
6868
gitignorePath := filepath.Join(dir, ".gitignore")
69-
data, err := os.ReadFile(gitignorePath) //nolint:gosec
69+
data, err := os.ReadFile(gitignorePath) // #nosec G304 -- gitignorePath is filepath.Join(dir, ".gitignore")
7070
if err != nil && !errors.Is(err, os.ErrNotExist) {
7171
return err
7272
}
@@ -80,7 +80,7 @@ func EnsureGitignore(dir string) error {
8080
content += "\n"
8181
}
8282
content += projectFile + "\n"
83-
return os.WriteFile(gitignorePath, []byte(content), 0644) //nolint:gosec
83+
return os.WriteFile(gitignorePath, []byte(content), 0644) // #nosec G306,G703 -- .gitignore must be world-readable; path is from filepath.Join
8484
}
8585

8686
func splitLines(s string) []string {

0 commit comments

Comments
 (0)