detached 18a731f

fix(lint): G115 safe stdin fd conversion, QF1012 use fmt.Fprintf directly

commit
18a731f4dde81ef4f394603d6313e8af24e6271a
author
bnema · · 2026-02-25 02:04 UTC
parent
1b136ca
signature
unsigned
9 files · +33 -15
M internal/adapters/dto/registry_token.go +1 -1
@@ -3,7 +3,7 @@ package dto
33 // TokenResponse represents the response from the token server.
44 type TokenResponse struct {
55 Token string `json:"token"`
6- AccessToken string `json:"access_token,omitempty"`
6+ AccessToken string `json:"access_token,omitempty"` //nolint:gosec // OCI token response field, required by Docker Registry API v2 spec
77 ExpiresIn int `json:"expires_in,omitempty"`
88 IssuedAt string `json:"issued_at,omitempty"`
99 }
@@ -3,7 +3,7 @@ package dto
3 // TokenResponse represents the response from the token server.3 // TokenResponse represents the response from the token server.
4 type TokenResponse struct {4 type TokenResponse struct {
5 Token string `json:"token"`5 Token string `json:"token"`
6- AccessToken string `json:"access_token,omitempty"`6+ AccessToken string `json:"access_token,omitempty"` //nolint:gosec // OCI token response field, required by Docker Registry API v2 spec
7 ExpiresIn int `json:"expires_in,omitempty"`7 ExpiresIn int `json:"expires_in,omitempty"`
8 IssuedAt string `json:"issued_at,omitempty"`8 IssuedAt string `json:"issued_at,omitempty"`
9 }9 }
M internal/adapters/in/cli/auth.go +20 -2
@@ -24,6 +24,16 @@ import (
2424 "github.com/bnema/gordon/pkg/duration"
2525 )
2626
27+// stdinFD returns os.Stdin's file descriptor as an int, guarded against
28+// uintptr overflow on platforms where uintptr > int.
29+func stdinFD() (int, error) {
30+ fd := os.Stdin.Fd()
31+ if fd > uintptr(^uint(0)>>1) {
32+ return 0, fmt.Errorf("stdin fd value %d overflows int", fd)
33+ }
34+ return int(fd), nil
35+}
36+
2737 // newAuthCmd creates the auth command group.
2838 func newAuthCmd() *cobra.Command {
2939 cmd := &cobra.Command{
@@ -274,7 +284,11 @@ func runAuthLogin(remoteName, username, password string) error {
274284 if password == "" {
275285 // Prompt for password (hidden input)
276286 fmt.Print("Password: ")
277- passwordBytes, err := term.ReadPassword(int(os.Stdin.Fd()))
287+ fd, err := stdinFD()
288+ if err != nil {
289+ return fmt.Errorf("failed to get stdin fd: %w", err)
290+ }
291+ passwordBytes, err := term.ReadPassword(fd)
278292 if err != nil {
279293 // Fallback for non-terminal input
280294 reader := bufio.NewReader(os.Stdin)
@@ -595,7 +609,11 @@ func runPasswordHash() error {
595609 fmt.Print("Enter password: ")
596610
597611 // Read password without echo (use os.Stdin.Fd() for better compatibility)
598- passwordBytes, err := term.ReadPassword(int(os.Stdin.Fd()))
612+ fd, err := stdinFD()
613+ if err != nil {
614+ return fmt.Errorf("failed to get stdin fd: %w", err)
615+ }
616+ passwordBytes, err := term.ReadPassword(fd)
599617 if err != nil {
600618 // Fallback for non-terminal input
601619 reader := bufio.NewReader(os.Stdin)
@@ -24,6 +24,16 @@ import (
24 "github.com/bnema/gordon/pkg/duration"24 "github.com/bnema/gordon/pkg/duration"
25 )25 )
26 26
27+// stdinFD returns os.Stdin's file descriptor as an int, guarded against
28+// uintptr overflow on platforms where uintptr > int.
29+func stdinFD() (int, error) {
30+ fd := os.Stdin.Fd()
31+ if fd > uintptr(^uint(0)>>1) {
32+ return 0, fmt.Errorf("stdin fd value %d overflows int", fd)
33+ }
34+ return int(fd), nil
35+}
36+
27 // newAuthCmd creates the auth command group.37 // newAuthCmd creates the auth command group.
28 func newAuthCmd() *cobra.Command {38 func newAuthCmd() *cobra.Command {
29 cmd := &cobra.Command{39 cmd := &cobra.Command{
@@ -274,7 +284,11 @@ func runAuthLogin(remoteName, username, password string) error {
274 if password == "" {284 if password == "" {
275 // Prompt for password (hidden input)285 // Prompt for password (hidden input)
276 fmt.Print("Password: ")286 fmt.Print("Password: ")
277- passwordBytes, err := term.ReadPassword(int(os.Stdin.Fd()))287+ fd, err := stdinFD()
288+ if err != nil {
289+ return fmt.Errorf("failed to get stdin fd: %w", err)
290+ }
291+ passwordBytes, err := term.ReadPassword(fd)
278 if err != nil {292 if err != nil {
279 // Fallback for non-terminal input293 // Fallback for non-terminal input
280 reader := bufio.NewReader(os.Stdin)294 reader := bufio.NewReader(os.Stdin)
@@ -595,7 +609,11 @@ func runPasswordHash() error {
595 fmt.Print("Enter password: ")609 fmt.Print("Enter password: ")
596 610
597 // Read password without echo (use os.Stdin.Fd() for better compatibility)611 // Read password without echo (use os.Stdin.Fd() for better compatibility)
598- passwordBytes, err := term.ReadPassword(int(os.Stdin.Fd()))612+ fd, err := stdinFD()
613+ if err != nil {
614+ return fmt.Errorf("failed to get stdin fd: %w", err)
615+ }
616+ passwordBytes, err := term.ReadPassword(fd)
599 if err != nil {617 if err != nil {
600 // Fallback for non-terminal input618 // Fallback for non-terminal input
601 reader := bufio.NewReader(os.Stdin)619 reader := bufio.NewReader(os.Stdin)
M internal/adapters/in/cli/push.go +2 -2
@@ -724,14 +724,14 @@ func getGitVersion(ctx context.Context) string {
724724 }
725725
726726 func dockerTag(ctx context.Context, src, dst string) error {
727- cmd := exec.CommandContext(ctx, "docker", "tag", src, dst)
727+ cmd := exec.CommandContext(ctx, "docker", "tag", src, dst) //nolint:gosec // binary is constant; image refs validated by OCI ref parser
728728 cmd.Stdout = os.Stdout
729729 cmd.Stderr = os.Stderr
730730 return cmd.Run()
731731 }
732732
733733 func dockerPush(ctx context.Context, ref string) error {
734- cmd := exec.CommandContext(ctx, "docker", "push", ref)
734+ cmd := exec.CommandContext(ctx, "docker", "push", ref) //nolint:gosec // binary is constant; image ref validated by OCI ref parser
735735 cmd.Stdout = os.Stdout
736736 cmd.Stderr = os.Stderr
737737 return cmd.Run()
@@ -724,14 +724,14 @@ func getGitVersion(ctx context.Context) string {
724 }724 }
725 725
726 func dockerTag(ctx context.Context, src, dst string) error {726 func dockerTag(ctx context.Context, src, dst string) error {
727- cmd := exec.CommandContext(ctx, "docker", "tag", src, dst)727+ cmd := exec.CommandContext(ctx, "docker", "tag", src, dst) //nolint:gosec // binary is constant; image refs validated by OCI ref parser
728 cmd.Stdout = os.Stdout728 cmd.Stdout = os.Stdout
729 cmd.Stderr = os.Stderr729 cmd.Stderr = os.Stderr
730 return cmd.Run()730 return cmd.Run()
731 }731 }
732 732
733 func dockerPush(ctx context.Context, ref string) error {733 func dockerPush(ctx context.Context, ref string) error {
734- cmd := exec.CommandContext(ctx, "docker", "push", ref)734+ cmd := exec.CommandContext(ctx, "docker", "push", ref) //nolint:gosec // binary is constant; image ref validated by OCI ref parser
735 cmd.Stdout = os.Stdout735 cmd.Stdout = os.Stdout
736 cmd.Stderr = os.Stderr736 cmd.Stderr = os.Stderr
737 return cmd.Run()737 return cmd.Run()
M internal/adapters/in/cli/remote/auth.go +1 -1
@@ -12,7 +12,7 @@ import (
1212 // PasswordRequest represents the request body for POST /auth/password.
1313 type PasswordRequest struct {
1414 Username string `json:"username"`
15- Password string `json:"password"`
15+ Password string `json:"password"` //nolint:gosec // intentional: CLI credential DTO for auth endpoint
1616 }
1717
1818 // PasswordResponse represents the response from POST /auth/password.
@@ -12,7 +12,7 @@ import (
12 // PasswordRequest represents the request body for POST /auth/password.12 // PasswordRequest represents the request body for POST /auth/password.
13 type PasswordRequest struct {13 type PasswordRequest struct {
14 Username string `json:"username"`14 Username string `json:"username"`
15- Password string `json:"password"`15+ Password string `json:"password"` //nolint:gosec // intentional: CLI credential DTO for auth endpoint
16 }16 }
17 17
18 // PasswordResponse represents the response from POST /auth/password.18 // PasswordResponse represents the response from POST /auth/password.
M internal/adapters/in/cli/ui/components/selector.go +1 -1
@@ -82,7 +82,7 @@ func (m SelectorModel) View() string {
8282 if i == m.cursor {
8383 b.WriteString(styles.Theme.Highlight.Render(cursor + line))
8484 } else {
85- b.WriteString(fmt.Sprintf("%s%s", cursor, line))
85+ fmt.Fprintf(&b, "%s%s", cursor, line)
8686 }
8787 b.WriteString("\n")
8888 }
@@ -82,7 +82,7 @@ func (m SelectorModel) View() string {
82 if i == m.cursor {82 if i == m.cursor {
83 b.WriteString(styles.Theme.Highlight.Render(cursor + line))83 b.WriteString(styles.Theme.Highlight.Render(cursor + line))
84 } else {84 } else {
85- b.WriteString(fmt.Sprintf("%s%s", cursor, line))85+ fmt.Fprintf(&b, "%s%s", cursor, line)
86 }86 }
87 b.WriteString("\n")87 b.WriteString("\n")
88 }88 }
M internal/adapters/out/domainsecrets/pass_store.go +4 -4
@@ -646,7 +646,7 @@ func (s *PassStore) ManifestExists(domainName string) (bool, error) {
646646 }
647647
648648 func (s *PassStore) passInsert(ctx context.Context, path, value string) error {
649- cmd := exec.CommandContext(ctx, "pass", "insert", "-m", "-f", path)
649+ cmd := exec.CommandContext(ctx, "pass", "insert", "-m", "-f", path) //nolint:gosec // binary is constant ("pass"); path arguments validated by secrets path validator
650650 cmd.Stdin = strings.NewReader(value)
651651 output, err := cmd.CombinedOutput()
652652 if err != nil {
@@ -656,7 +656,7 @@ func (s *PassStore) passInsert(ctx context.Context, path, value string) error {
656656 }
657657
658658 func (s *PassStore) passRemove(ctx context.Context, path string) error {
659- cmd := exec.CommandContext(ctx, "pass", "rm", "-f", path)
659+ cmd := exec.CommandContext(ctx, "pass", "rm", "-f", path) //nolint:gosec // binary is constant ("pass"); path arguments validated by secrets path validator
660660 output, err := cmd.CombinedOutput()
661661 if err != nil {
662662 if passEntryMissing(string(output)) {
@@ -668,7 +668,7 @@ func (s *PassStore) passRemove(ctx context.Context, path string) error {
668668 }
669669
670670 func (s *PassStore) passShow(ctx context.Context, path string) (string, bool, error) {
671- cmd := exec.CommandContext(ctx, "pass", "show", path)
671+ cmd := exec.CommandContext(ctx, "pass", "show", path) //nolint:gosec // binary is constant ("pass"); path arguments validated by secrets path validator
672672 output, err := cmd.CombinedOutput()
673673 if err != nil {
674674 if passEntryMissing(string(output)) {
@@ -687,7 +687,7 @@ func (s *PassStore) listTopLevelEntries(ctx context.Context, basePath string) ([
687687 return nil, err
688688 }
689689
690- cmd := exec.CommandContext(ctx, "pass", "ls", basePath)
690+ cmd := exec.CommandContext(ctx, "pass", "ls", basePath) //nolint:gosec // binary is constant ("pass"); path arguments validated by secrets path validator
691691 output, err := cmd.CombinedOutput()
692692 if err != nil {
693693 if passEntryMissing(string(output)) {
@@ -646,7 +646,7 @@ func (s *PassStore) ManifestExists(domainName string) (bool, error) {
646 }646 }
647 647
648 func (s *PassStore) passInsert(ctx context.Context, path, value string) error {648 func (s *PassStore) passInsert(ctx context.Context, path, value string) error {
649- cmd := exec.CommandContext(ctx, "pass", "insert", "-m", "-f", path)649+ cmd := exec.CommandContext(ctx, "pass", "insert", "-m", "-f", path) //nolint:gosec // binary is constant ("pass"); path arguments validated by secrets path validator
650 cmd.Stdin = strings.NewReader(value)650 cmd.Stdin = strings.NewReader(value)
651 output, err := cmd.CombinedOutput()651 output, err := cmd.CombinedOutput()
652 if err != nil {652 if err != nil {
@@ -656,7 +656,7 @@ func (s *PassStore) passInsert(ctx context.Context, path, value string) error {
656 }656 }
657 657
658 func (s *PassStore) passRemove(ctx context.Context, path string) error {658 func (s *PassStore) passRemove(ctx context.Context, path string) error {
659- cmd := exec.CommandContext(ctx, "pass", "rm", "-f", path)659+ cmd := exec.CommandContext(ctx, "pass", "rm", "-f", path) //nolint:gosec // binary is constant ("pass"); path arguments validated by secrets path validator
660 output, err := cmd.CombinedOutput()660 output, err := cmd.CombinedOutput()
661 if err != nil {661 if err != nil {
662 if passEntryMissing(string(output)) {662 if passEntryMissing(string(output)) {
@@ -668,7 +668,7 @@ func (s *PassStore) passRemove(ctx context.Context, path string) error {
668 }668 }
669 669
670 func (s *PassStore) passShow(ctx context.Context, path string) (string, bool, error) {670 func (s *PassStore) passShow(ctx context.Context, path string) (string, bool, error) {
671- cmd := exec.CommandContext(ctx, "pass", "show", path)671+ cmd := exec.CommandContext(ctx, "pass", "show", path) //nolint:gosec // binary is constant ("pass"); path arguments validated by secrets path validator
672 output, err := cmd.CombinedOutput()672 output, err := cmd.CombinedOutput()
673 if err != nil {673 if err != nil {
674 if passEntryMissing(string(output)) {674 if passEntryMissing(string(output)) {
@@ -687,7 +687,7 @@ func (s *PassStore) listTopLevelEntries(ctx context.Context, basePath string) ([
687 return nil, err687 return nil, err
688 }688 }
689 689
690- cmd := exec.CommandContext(ctx, "pass", "ls", basePath)690+ cmd := exec.CommandContext(ctx, "pass", "ls", basePath) //nolint:gosec // binary is constant ("pass"); path arguments validated by secrets path validator
691 output, err := cmd.CombinedOutput()691 output, err := cmd.CombinedOutput()
692 if err != nil {692 if err != nil {
693 if passEntryMissing(string(output)) {693 if passEntryMissing(string(output)) {
M internal/adapters/out/secrets/pass.go +1 -1
@@ -80,7 +80,7 @@ func (p *PassProvider) GetSecret(ctx context.Context, path string) (string, erro
8080 ctx, cancel := context.WithTimeout(ctx, p.timeout)
8181 defer cancel()
8282
83- cmd := exec.CommandContext(ctx, "pass", "show", path)
83+ cmd := exec.CommandContext(ctx, "pass", "show", path) //nolint:gosec // binary is constant ("pass"); arguments are validated secret paths
8484 output, err := cmd.Output()
8585 if err != nil {
8686 if exitError, ok := err.(*exec.ExitError); ok {
@@ -80,7 +80,7 @@ func (p *PassProvider) GetSecret(ctx context.Context, path string) (string, erro
80 ctx, cancel := context.WithTimeout(ctx, p.timeout)80 ctx, cancel := context.WithTimeout(ctx, p.timeout)
81 defer cancel()81 defer cancel()
82 82
83- cmd := exec.CommandContext(ctx, "pass", "show", path)83+ cmd := exec.CommandContext(ctx, "pass", "show", path) //nolint:gosec // binary is constant ("pass"); arguments are validated secret paths
84 output, err := cmd.Output()84 output, err := cmd.Output()
85 if err != nil {85 if err != nil {
86 if exitError, ok := err.(*exec.ExitError); ok {86 if exitError, ok := err.(*exec.ExitError); ok {
M internal/adapters/out/tokenstore/pass.go +2 -2
@@ -488,7 +488,7 @@ func (s *PassStore) DeleteToken(ctx context.Context, subject string) error {
488488
489489 // passInsert inserts a value into pass.
490490 func (s *PassStore) passInsert(ctx context.Context, path, value string) error {
491- cmd := exec.CommandContext(ctx, "pass", "insert", "-m", "-f", path)
491+ cmd := exec.CommandContext(ctx, "pass", "insert", "-m", "-f", path) //nolint:gosec // binary is constant ("pass"); path arguments are sanitized token subjects
492492 cmd.Stdin = strings.NewReader(value)
493493 output, err := cmd.CombinedOutput()
494494 if err != nil {
@@ -499,7 +499,7 @@ func (s *PassStore) passInsert(ctx context.Context, path, value string) error {
499499
500500 // passShow retrieves a value from pass.
501501 func (s *PassStore) passShow(ctx context.Context, path string) (string, error) {
502- cmd := exec.CommandContext(ctx, "pass", "show", path)
502+ cmd := exec.CommandContext(ctx, "pass", "show", path) //nolint:gosec // binary is constant ("pass"); path arguments are sanitized token subjects
503503 output, err := cmd.Output()
504504 if err != nil {
505505 return "", fmt.Errorf("pass show failed: %w", err)
@@ -488,7 +488,7 @@ func (s *PassStore) DeleteToken(ctx context.Context, subject string) error {
488 488
489 // passInsert inserts a value into pass.489 // passInsert inserts a value into pass.
490 func (s *PassStore) passInsert(ctx context.Context, path, value string) error {490 func (s *PassStore) passInsert(ctx context.Context, path, value string) error {
491- cmd := exec.CommandContext(ctx, "pass", "insert", "-m", "-f", path)491+ cmd := exec.CommandContext(ctx, "pass", "insert", "-m", "-f", path) //nolint:gosec // binary is constant ("pass"); path arguments are sanitized token subjects
492 cmd.Stdin = strings.NewReader(value)492 cmd.Stdin = strings.NewReader(value)
493 output, err := cmd.CombinedOutput()493 output, err := cmd.CombinedOutput()
494 if err != nil {494 if err != nil {
@@ -499,7 +499,7 @@ func (s *PassStore) passInsert(ctx context.Context, path, value string) error {
499 499
500 // passShow retrieves a value from pass.500 // passShow retrieves a value from pass.
501 func (s *PassStore) passShow(ctx context.Context, path string) (string, error) {501 func (s *PassStore) passShow(ctx context.Context, path string) (string, error) {
502- cmd := exec.CommandContext(ctx, "pass", "show", path)502+ cmd := exec.CommandContext(ctx, "pass", "show", path) //nolint:gosec // binary is constant ("pass"); path arguments are sanitized token subjects
503 output, err := cmd.Output()503 output, err := cmd.Output()
504 if err != nil {504 if err != nil {
505 return "", fmt.Errorf("pass show failed: %w", err)505 return "", fmt.Errorf("pass show failed: %w", err)
M internal/usecase/container/autoroute.go +1 -1
@@ -493,7 +493,7 @@ func writeEnvFile(path string, env map[string]string) error {
493493 if strings.ContainsAny(v, " \t\n\"'$\\") {
494494 v = fmt.Sprintf("\"%s\"", strings.ReplaceAll(v, "\"", "\\\""))
495495 }
496- buf.WriteString(fmt.Sprintf("%s=%s\n", k, v))
496+ fmt.Fprintf(&buf, "%s=%s\n", k, v)
497497 }
498498
499499 return os.WriteFile(path, buf.Bytes(), 0600)
@@ -493,7 +493,7 @@ func writeEnvFile(path string, env map[string]string) error {
493 if strings.ContainsAny(v, " \t\n\"'$\\") {493 if strings.ContainsAny(v, " \t\n\"'$\\") {
494 v = fmt.Sprintf("\"%s\"", strings.ReplaceAll(v, "\"", "\\\""))494 v = fmt.Sprintf("\"%s\"", strings.ReplaceAll(v, "\"", "\\\""))
495 }495 }
496- buf.WriteString(fmt.Sprintf("%s=%s\n", k, v))496+ fmt.Fprintf(&buf, "%s=%s\n", k, v)
497 }497 }
498 498
499 return os.WriteFile(path, buf.Bytes(), 0600)499 return os.WriteFile(path, buf.Bytes(), 0600)

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close