@@ -2,8 +2,11 @@ package git_clone
22
33import (
44 "fmt"
5+ "io"
6+ "net/url"
57 "os"
68 "path/filepath"
9+ "strings"
710
811 l "github.com/konflux-ci/konflux-build-cli/pkg/logger"
912)
@@ -45,24 +48,192 @@ func (c *GitClone) cleanCheckoutDir() error {
4548}
4649
4750// setupGitConfig configures git settings for SSL verification and CA bundle.
51+ // Uses environment variables instead of git config to avoid modifying global git state.
4852func (c * GitClone ) setupGitConfig () error {
4953 if ! c .Params .SSLVerify {
50- l .Logger .Info ("Disabling SSL verification (http.sslVerify=false )" )
51- if err := c . CliWrappers . GitCli . ConfigLocal ( " " , "http.sslVerify" , "false " ); err != nil {
52- return fmt . Errorf ( "failed to configure http.sslVerify: %w" , err )
54+ l .Logger .Info ("Disabling SSL verification (GIT_SSL_NO_VERIFY=true )" )
55+ if err := os . Setenv ( "GIT_SSL_NO_VERIFY " , "true " ); err != nil {
56+ return err
5357 }
5458 }
5559
56- caBundlePath := c .Params .CaBundlePath
57- if caBundlePath == "" {
58- caBundlePath = "/mnt/trusted-ca/ca- bundle.crt"
59- }
60- if _ , err := os . Stat ( caBundlePath ); err == nil {
61- l . Logger . Infof ( "Using mounted CA bundle: %s" , caBundlePath )
62- if err := c . CliWrappers . GitCli . ConfigLocal ( "" , "http.sslCAInfo" , caBundlePath ); err != nil {
63- return fmt . Errorf ( "failed to configure http.sslCAInfo : %w " , err )
60+ if c .Params .CaBundlePath != "" {
61+ if _ , err := os . Stat ( c . Params . CaBundlePath ); err == nil {
62+ l . Logger . Infof ( "Using CA bundle: %s" , c . Params . CaBundlePath )
63+ if err := os . Setenv ( "GIT_SSL_CAINFO" , c . Params . CaBundlePath ); err != nil {
64+ return err
65+ }
66+ } else {
67+ l . Logger . Warnf ( "CA bundle path specified but not found : %s " , c . Params . CaBundlePath )
6468 }
6569 }
6670
6771 return nil
6872}
73+
74+ // setupBasicAuth sets up git credentials from a basic-auth workspace.
75+ // Supports two formats:
76+ // 1. .git-credentials and .gitconfig files (copied directly)
77+ // 2. username and password files (kubernetes.io/basic-auth secret format)
78+ func (c * GitClone ) setupBasicAuth () error {
79+ if c .Params .BasicAuthDirectory == "" {
80+ return nil
81+ }
82+
83+ authDir := c .Params .BasicAuthDirectory
84+
85+ if _ , err := os .Stat (authDir ); os .IsNotExist (err ) {
86+ l .Logger .Infof ("Basic auth directory not found: %s" , authDir )
87+ return nil
88+ }
89+
90+ gitCredentialsPath := filepath .Join (authDir , ".git-credentials" )
91+ gitConfigPath := filepath .Join (authDir , ".gitconfig" )
92+ usernamePath := filepath .Join (authDir , "username" )
93+ passwordPath := filepath .Join (authDir , "password" )
94+
95+ destCredentials := filepath .Join (c .internalDir , ".git-credentials" )
96+ destConfig := filepath .Join (c .internalDir , ".gitconfig" )
97+
98+ // Format 1: .git-credentials and .gitconfig files
99+ if fileExists (gitCredentialsPath ) && fileExists (gitConfigPath ) {
100+ l .Logger .Info ("Setting up basic auth from .git-credentials and .gitconfig" )
101+
102+ if err := copyFile (gitCredentialsPath , destCredentials , 0400 ); err != nil {
103+ return fmt .Errorf ("failed to copy .git-credentials: %w" , err )
104+ }
105+
106+ configContent , err := readFileWithLimit (gitConfigPath , maxAuthFileSize )
107+ if err != nil {
108+ return fmt .Errorf ("failed to read .gitconfig: %w" , err )
109+ }
110+ rewritten := rewriteGitConfigCredentialHelper (string (configContent ), destCredentials )
111+ if err := os .WriteFile (destConfig , []byte (rewritten ), 0400 ); err != nil {
112+ return fmt .Errorf ("failed to write .gitconfig: %w" , err )
113+ }
114+
115+ if err := os .Setenv ("GIT_CONFIG_GLOBAL" , destConfig ); err != nil {
116+ return err
117+ }
118+
119+ l .Logger .Info ("Basic auth credentials configured" )
120+ return nil
121+ }
122+
123+ // Format 2: kubernetes.io/basic-auth secret (username and password files)
124+ if fileExists (usernamePath ) && fileExists (passwordPath ) {
125+ l .Logger .Info ("Setting up basic auth from username/password files" )
126+
127+ username , err := readFileWithLimit (usernamePath , maxAuthFileSize )
128+ if err != nil {
129+ return fmt .Errorf ("failed to read username file: %w" , err )
130+ }
131+
132+ password , err := readFileWithLimit (passwordPath , maxAuthFileSize )
133+ if err != nil {
134+ return fmt .Errorf ("failed to read password file: %w" , err )
135+ }
136+
137+ parsedURL , err := url .Parse (c .Params .URL )
138+ if err != nil {
139+ return fmt .Errorf ("failed to parse repository URL: %w" , err )
140+ }
141+ hostname := parsedURL .Host
142+
143+ credentialsContent := fmt .Sprintf ("https://%s:%s@%s\n " ,
144+ url .PathEscape (strings .TrimSpace (string (username ))),
145+ url .PathEscape (strings .TrimSpace (string (password ))),
146+ hostname )
147+
148+ if err := os .WriteFile (destCredentials , []byte (credentialsContent ), 0400 ); err != nil {
149+ return fmt .Errorf ("failed to write .git-credentials: %w" , err )
150+ }
151+
152+ gitConfigContent := fmt .Sprintf ("[credential \" https://%s\" ]\n helper = store --file=%s\n " , hostname , destCredentials )
153+ if err := os .WriteFile (destConfig , []byte (gitConfigContent ), 0400 ); err != nil {
154+ return fmt .Errorf ("failed to write .gitconfig: %w" , err )
155+ }
156+
157+ if err := os .Setenv ("GIT_CONFIG_GLOBAL" , destConfig ); err != nil {
158+ return err
159+ }
160+
161+ l .Logger .Infof ("Basic auth credentials configured for %s" , hostname )
162+ return nil
163+ }
164+
165+ return fmt .Errorf ("unknown basic-auth workspace format: expected .git-credentials/.gitconfig or username/password files" )
166+ }
167+
168+ // rewriteGitConfigCredentialHelper rewrites "helper = store" lines in a git config
169+ // to include an explicit --file flag pointing to the given credentials path.
170+ func rewriteGitConfigCredentialHelper (configContent , credentialsPath string ) string {
171+ lines := strings .Split (configContent , "\n " )
172+ for i , line := range lines {
173+ trimmed := strings .TrimSpace (line )
174+ if trimmed == "helper = store" {
175+ indent := line [:len (line )- len (strings .TrimLeft (line , " \t " ))]
176+ lines [i ] = fmt .Sprintf ("%shelper = store --file=%s" , indent , credentialsPath )
177+ }
178+ }
179+ return strings .Join (lines , "\n " )
180+ }
181+
182+ // fileExists checks if a file exists and is not a directory.
183+ func fileExists (path string ) bool {
184+ info , err := os .Stat (path )
185+ if os .IsNotExist (err ) {
186+ return false
187+ }
188+ return err == nil && ! info .IsDir ()
189+ }
190+
191+ // maxAuthFileSize is the maximum allowed size for auth-related files
192+ // (.gitconfig, .git-credentials, SSH keys, username, password).
193+ const maxAuthFileSize = 1 << 20 // 1MB
194+
195+ // readFileWithLimit reads a file, rejecting files larger than maxSize.
196+ // Uses file-descriptor-based stat and limited read to avoid TOCTOU races.
197+ func readFileWithLimit (path string , maxSize int64 ) (data []byte , err error ) {
198+ f , err := os .Open (path )
199+ if err != nil {
200+ return nil , err
201+ }
202+ defer func () {
203+ if cerr := f .Close (); cerr != nil && err == nil {
204+ err = cerr
205+ }
206+ }()
207+
208+ info , err := f .Stat ()
209+ if err != nil {
210+ return nil , err
211+ }
212+ if info .Size () > maxSize {
213+ return nil , fmt .Errorf ("authentication file exceeds maximum allowed size (%d bytes)" , maxSize )
214+ }
215+
216+ // Use LimitReader to enforce the size cap during read, even if the file
217+ // was extended between Stat and Read (belt-and-suspenders).
218+ data , err = io .ReadAll (io .LimitReader (f , maxSize + 1 ))
219+ if err != nil {
220+ return nil , err
221+ }
222+ if int64 (len (data )) > maxSize {
223+ return nil , fmt .Errorf ("authentication file exceeds maximum allowed size (%d bytes)" , maxSize )
224+ }
225+ return data , nil
226+ }
227+
228+ // copyFile copies a file from src to dest with the specified permissions.
229+ func copyFile (src , dest string , perm os.FileMode ) error {
230+ if err := os .MkdirAll (filepath .Dir (dest ), 0755 ); err != nil {
231+ return err
232+ }
233+
234+ data , err := readFileWithLimit (src , maxAuthFileSize )
235+ if err != nil {
236+ return err
237+ }
238+ return os .WriteFile (dest , data , perm )
239+ }
0 commit comments