Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
77bede9
feat: password sanitization
Ash-exp Dec 18, 2023
12d497c
updated docker login
Ash-exp Dec 19, 2023
3f7b9e8
fixed: format
Ash-exp Dec 19, 2023
7988261
fixed: updated gitcli
Ash-exp Dec 19, 2023
275e6c9
fixed: git cli issue
Ash-exp Dec 19, 2023
f298e8f
sanitised buildx commands
Ash-exp Dec 19, 2023
21f3cfa
added comments and fixed build pack cmds
Ash-exp Dec 20, 2023
4c7ca25
fixed: buildx k8s driver cmd
Ash-exp Dec 20, 2023
49472de
sanitized
Ash-exp Dec 22, 2023
eda3833
Merge branch 'main' into feat-password-sanitization
Ash-exp Jan 2, 2024
db9823f
fixed: docker push command
Ash-exp Jan 9, 2024
893b747
fixed: docker command
Ash-exp Jan 9, 2024
544476c
Refactored: cli commands
Ash-exp Jan 11, 2024
0e559e2
fixed: docker daemon command
Ash-exp Jan 11, 2024
c0da9c8
added empty arg check to cmd
Ash-exp Jan 11, 2024
384c372
fixed: docker stop command
Ash-exp Jan 11, 2024
efe62ed
added logs for debugging
Ash-exp Jan 11, 2024
c2fc9de
added: todo
Ash-exp Jan 11, 2024
2d753af
err log added
Ash-exp Jan 11, 2024
c69e8ea
added: log
Ash-exp Jan 11, 2024
5051979
fixed: docker daemon command
Ash-exp Jan 12, 2024
41c9945
reverted: docker daemon command
Ash-exp Jan 12, 2024
f62fa4a
debug: docker daemon command
Jan 12, 2024
e2d8c9b
removed: debug logs
Jan 12, 2024
8060139
debug: git merge logs
Jan 12, 2024
478119e
reverted: debug commits
Ash-exp Jan 12, 2024
4762af6
updated: LOCAL_BUILDX_LOCATION value
Ash-exp Jan 12, 2024
a15cb45
feat: updated checkAndCreateDirectory
Ash-exp Jan 12, 2024
fdff04f
feat: updated checkAndCreateDirectory
Ash-exp Jan 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 38 additions & 22 deletions helper/DockerHelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ func StartDockerDaemon(dockerConnection, dockerRegistryUrl, dockerCert, defaultA
}
dockerdstart = fmt.Sprintf("dockerd %s --host=unix:///var/run/docker.sock %s --host=tcp://0.0.0.0:2375 > /usr/local/bin/nohup.out 2>&1 &", defaultAddressPoolFlag, dockerMtuValueFlag)
}
// TODO: refactor dockerBuild string to []string for removing script injection
// Until then use fmt.Sprintf("%q", userInput) to sanitize the input
out, _ := exec.Command("/bin/sh", "-c", dockerdstart).Output()
log.Println(string(out))
waitForDockerDaemon(util.RETRYCOUNT)
Expand Down Expand Up @@ -171,8 +173,7 @@ func DockerLogin(dockerCredentials *DockerCredentials) error {
pwd = pwd[:len(pwd)-1]
}
}
dockerLogin := fmt.Sprintf("docker login -u '%s' -p '%s' '%s' ", username, pwd, dockerCredentials.DockerRegistryURL)
awsLoginCmd := exec.Command("/bin/sh", "-c", dockerLogin)
awsLoginCmd := exec.Command("/bin/sh", "-c", "docker", "login", "-u", username, "-p", pwd, dockerCredentials.DockerRegistryURL)
err := util.RunCommand(awsLoginCmd)
if err != nil {
log.Println(err)
Expand Down Expand Up @@ -212,6 +213,8 @@ func BuildArtifact(ciRequest *CommonWorkflowRequest) (string, error) {
return "", err
}
if ciBuildConfig.CiBuildType == SELF_DOCKERFILE_BUILD_TYPE || ciBuildConfig.CiBuildType == MANAGED_DOCKERFILE_BUILD_TYPE {
// TODO: refactor dockerBuild string to []string for removing script injection
// Until then use fmt.Sprintf("%q", userInput) to sanitize the input
dockerBuild := "docker build "
if ciRequest.CacheInvalidate && ciRequest.IsPvcMounted {
dockerBuild = dockerBuild + "--no-cache "
Expand All @@ -228,28 +231,28 @@ func BuildArtifact(ciRequest *CommonWorkflowRequest) (string, error) {
dockerBuild = dockerBuildxBuild + " "
}
if isTargetPlatformSet {
dockerBuild += "--platform " + dockerBuildConfig.TargetPlatform + " "
dockerBuild += fmt.Sprintf("--platform %q ", dockerBuildConfig.TargetPlatform)
}
}
dockerBuildFlags := make(map[string]string)
dockerBuildArgsMap := dockerBuildConfig.Args
for k, v := range dockerBuildArgsMap {
flagKey := fmt.Sprintf("%s %s", BUILD_ARG_FLAG, k)
flagKey := fmt.Sprintf("%s %q", BUILD_ARG_FLAG, strings.TrimSpace(k))
if strings.HasPrefix(v, DEVTRON_ENV_VAR_PREFIX) {
valueFromEnv := os.Getenv(strings.TrimPrefix(v, DEVTRON_ENV_VAR_PREFIX))
dockerBuildFlags[flagKey] = fmt.Sprintf("=\"%s\"", valueFromEnv)
dockerBuildFlags[flagKey] = fmt.Sprintf("=\"%s\"", strings.TrimSpace(valueFromEnv))
} else {
dockerBuildFlags[flagKey] = fmt.Sprintf("=%s", v)
dockerBuildFlags[flagKey] = fmt.Sprintf("=%s", strings.TrimSpace(v))
}
}
dockerBuildOptionsMap := dockerBuildConfig.DockerBuildOptions
for k, v := range dockerBuildOptionsMap {
flagKey := "--" + k
flagKey := "--" + strings.TrimSpace(k)
if strings.HasPrefix(v, DEVTRON_ENV_VAR_PREFIX) {
valueFromEnv := os.Getenv(strings.TrimPrefix(v, DEVTRON_ENV_VAR_PREFIX))
dockerBuildFlags[flagKey] = fmt.Sprintf("=%s", valueFromEnv)
dockerBuildFlags[flagKey] = fmt.Sprintf("=%s", strings.TrimSpace(valueFromEnv))
} else {
dockerBuildFlags[flagKey] = fmt.Sprintf("=%s", v)
dockerBuildFlags[flagKey] = fmt.Sprintf("=%s", strings.TrimSpace(v))
}
}
for key, value := range dockerBuildFlags {
Expand Down Expand Up @@ -295,7 +298,7 @@ func BuildArtifact(ciRequest *CommonWorkflowRequest) (string, error) {

dockerBuild = getBuildxBuildCommand(cacheEnabled, dockerBuild, oldCacheBuildxPath, localCachePath, dest, dockerBuildConfig)
} else {
dockerBuild = fmt.Sprintf("%s -f %s --network host -t %s %s", dockerBuild, dockerBuildConfig.DockerfilePath, ciRequest.DockerRepository, dockerBuildConfig.BuildContext)
dockerBuild = fmt.Sprintf("%s -f %q --network host -t %q %s", dockerBuild, dockerBuildConfig.DockerfilePath, ciRequest.DockerRepository, dockerBuildConfig.BuildContext)
}
if envVars.ShowDockerBuildCmdInLogs {
log.Println("Starting docker build : ", dockerBuild)
Expand Down Expand Up @@ -327,24 +330,25 @@ func BuildArtifact(ciRequest *CommonWorkflowRequest) (string, error) {
projectPath = "./" + projectPath
}
handleLanguageVersion(projectPath, buildPackParams)
buildPackCmd := fmt.Sprintf("pack build %s --path %s --builder %s", dest, projectPath, buildPackParams.BuilderId)
// TODO: refactor buildPackCmd: string to []string for removing script injection
// Until then use fmt.Sprintf("%q", userInput) to sanitize the input
buildPackCmd := fmt.Sprintf("pack build %q --path %q --builder %q", dest, projectPath, buildPackParams.BuilderId)
BuildPackArgsMap := buildPackParams.Args
for k, v := range BuildPackArgsMap {
buildPackCmd = buildPackCmd + " --env " + k + "=" + v
buildPackCmd = buildPackCmd + " --env " + strings.TrimSpace(k) + "=" + strings.TrimSpace(v)
}

if len(buildPackParams.BuildPacks) > 0 {
for _, buildPack := range buildPackParams.BuildPacks {
buildPackCmd = buildPackCmd + " --buildpack " + buildPack
buildPackCmd = buildPackCmd + " --buildpack " + strings.TrimSpace(buildPack)
}
}
log.Println(" -----> " + buildPackCmd)
err = executeCmd(buildPackCmd)
if err != nil {
return "", err
}
builderRmCmdString := "docker image rm " + buildPackParams.BuilderId
builderRmCmd := exec.Command("/bin/sh", "-c", builderRmCmdString)
builderRmCmd := exec.Command("/bin/sh", "-c", "docker", "image", "rm", buildPackParams.BuilderId)
err := builderRmCmd.Run()
if err != nil {
return "", err
Expand All @@ -355,9 +359,9 @@ func BuildArtifact(ciRequest *CommonWorkflowRequest) (string, error) {
}

func getBuildxBuildCommand(cacheEnabled bool, dockerBuild, oldCacheBuildxPath, localCachePath, dest string, dockerBuildConfig *DockerBuildConfig) string {
dockerBuild = fmt.Sprintf("%s -f %s -t %s --push %s --network host --allow network.host --allow security.insecure", dockerBuild, dockerBuildConfig.DockerfilePath, dest, dockerBuildConfig.BuildContext)
dockerBuild = fmt.Sprintf("%s -f %q -t %q --push %q --network host --allow network.host --allow security.insecure", dockerBuild, dockerBuildConfig.DockerfilePath, dest, dockerBuildConfig.BuildContext)
if cacheEnabled {
dockerBuild = fmt.Sprintf("%s --cache-to=type=local,dest=%s,mode=max --cache-from=type=local,src=%s", dockerBuild, localCachePath, oldCacheBuildxPath)
dockerBuild = fmt.Sprintf("%s --cache-to=type=local,dest=%q,mode=max --cache-from=type=local,src=%q", dockerBuild, localCachePath, oldCacheBuildxPath)
}

provenanceFlag := dockerBuildConfig.GetProvenanceFlag()
Expand Down Expand Up @@ -423,13 +427,17 @@ func handleLanguageVersion(projectPath string, buildpackConfig *BuildPackConfig)
return
}
if strings.TrimSpace(string(outputBytes)) == "null" {
// TODO: refactor versionUpdateCmd string to []string for removing script injection
// Until then use fmt.Sprintf("%q", userInput) to sanitize the input
tmpJsonFile := "./tmp.json"
versionUpdateCmd := fmt.Sprintf("jq '.engines.node = \"%s\"' %s >%s", languageVersion, finalPath, tmpJsonFile)
err := executeCmd(versionUpdateCmd)
if err != nil {
log.Println("error occurred while inserting node version", "err", err)
return
}
// TODO: refactor fileReplaceCmd string to []string for removing script injection
// Until then use fmt.Sprintf("%q", userInput) to sanitize the input
fileReplaceCmd := fmt.Sprintf("mv %s %s", tmpJsonFile, finalPath)
err = executeCmd(fileReplaceCmd)
if err != nil {
Expand All @@ -445,7 +453,15 @@ func handleLanguageVersion(projectPath string, buildpackConfig *BuildPackConfig)

}

// executeCmd uses CLI to run git command and it is prone to script injection |
// Don'ts:
// 1- Never concatenate the whole cmd args into a single string and pass it as exec.Command(name, fmt.Sprintf("--flag1 %s --flag2 %s --flag3 %s", value1, value2, value3)) |
// DOs:
// 1- Break the command to name and []args as exec.Command(name, []arg...)
// 2- Use strings.TrimSpace() to build an user defined flags; e.g: fmt.Sprintf("--%s", strings.TrimSpace(userDefinedFlag))
// 3- In case a single arg contains multiple user defined inputs, then use fmt.Sprintf() with %q to sanitize user defined inputs; exec.Command(name, "--flag=", fmt.Sprintf("key1=%q,key2=%q,key3=%q", userDefinedArg-1, userDefinedArg-2, userDefinedArg-2))
func executeCmd(dockerBuild string) error {
// TODO: dockerBuild should be []string{arg...}
dockerBuildCMD := exec.Command("/bin/sh", "-c", dockerBuild)
err := util.RunCommand(dockerBuildCMD)
if err != nil {
Expand All @@ -455,7 +471,7 @@ func executeCmd(dockerBuild string) error {
}

func tagDockerBuild(dockerRepository string, dest string) error {
dockerTag := "docker tag " + dockerRepository + ":latest" + " " + dest
dockerTag := fmt.Sprintf("docker tag %q:latest %q", dockerRepository, dest)
log.Println(" -----> " + dockerTag)
dockerTagCMD := exec.Command("/bin/sh", "-c", dockerTag)
err := util.RunCommand(dockerTagCMD)
Expand Down Expand Up @@ -547,7 +563,7 @@ func BuildDockerImagePath(ciRequest *CommonWorkflowRequest) (string, error) {

func PushArtifact(dest string) error {
//awsLogin := "$(aws ecr get-login --no-include-email --region " + ciRequest.AwsRegion + ")"
dockerPush := "docker push " + dest
dockerPush := fmt.Sprintf("docker push %q", dest)
log.Println("-----> " + dockerPush)
dockerPushCMD := exec.Command("/bin/sh", "-c", dockerPush)
err := util.RunCommand(dockerPushCMD)
Expand Down Expand Up @@ -581,7 +597,7 @@ func ExtractDigestForBuildx(dest string) (string, error) {
}

func ExtractDigestUsingPull(dest string) (string, error) {
dockerPull := "docker pull " + dest
dockerPull := fmt.Sprintf("docker pull %q", dest)
dockerPullCmd := exec.Command("/bin/sh", "-c", dockerPull)
digest, err := runGetDockerImageDigest(dockerPullCmd)
if err != nil {
Expand Down Expand Up @@ -718,15 +734,15 @@ func runCmd(cmd string) (error, *bytes.Buffer) {
}

func getBuildxK8sDriverCmd(driverOpts map[string]string, ciPipelineId, ciWorkflowId int) string {
buildxCreate := "docker buildx create --buildkitd-flags '--allow-insecure-entitlement network.host --allow-insecure-entitlement security.insecure' --name=%s --driver=kubernetes --node=%s --bootstrap "
buildxCreate := "docker buildx create --buildkitd-flags '--allow-insecure-entitlement network.host --allow-insecure-entitlement security.insecure' --name=%s --driver=kubernetes --node=%q --bootstrap "
nodeName := driverOpts["node"]
if nodeName == "" {
nodeName = BUILDX_NODE_NAME + fmt.Sprintf("%v-%v", ciPipelineId, ciWorkflowId) + util.Generate(3) //need this to generate unique name for builder node in same builder.
}
buildxCreate = fmt.Sprintf(buildxCreate, BUILDX_K8S_DRIVER_NAME, nodeName)
platforms := driverOpts["platform"]
if platforms != "" {
buildxCreate += " --platform=%s "
buildxCreate += " --platform=%q "
buildxCreate = fmt.Sprintf(buildxCreate, platforms)
}
if len(driverOpts["driverOptions"]) > 0 {
Expand Down
57 changes: 53 additions & 4 deletions helper/GitCliHelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ func NewGitUtil() *GitUtil {

const GIT_AKS_PASS = "/git-ask-pass.sh"

// Fetch uses CLI to run git command and it is prone to script injection |
// Don'ts:
// 1- Never concatenate the whole cmd args into a single string and pass it as exec.Command(name, fmt.Sprintf("--flag1 %s --flag2 %s --flag3 %s", value1, value2, value3)) |
// DOs:
// 1- Break the command to name and []args as exec.Command(name, []arg...)
// 2- Use strings.TrimSpace() to build an user defined flags; e.g: fmt.Sprintf("--%s", strings.TrimSpace(userDefinedFlag))
// 3- In case a single arg contains multiple user defined inputs, then use fmt.Sprintf() with %q to sanitize user defined inputs; exec.Command(name, "--flag=", fmt.Sprintf("key1=%q,key2=%q,key3=%q", userDefinedArg-1, userDefinedArg-2, userDefinedArg-2))
func (impl *GitUtil) Fetch(gitContext GitContext, rootDir string) (response, errMsg string, err error) {
log.Println(util.DEVTRON, "git fetch ", "location", rootDir)
cmd := exec.Command("git", "-C", rootDir, "fetch", "origin", "--tags", "--force")
Expand All @@ -28,6 +35,13 @@ func (impl *GitUtil) Fetch(gitContext GitContext, rootDir string) (response, err
return output, "", nil
}

// Checkout uses CLI to run git command and it is prone to script injection |
// Don'ts:
// 1- Never concatenate the whole cmd args into a single string and pass it as exec.Command(name, fmt.Sprintf("--flag1 %s --flag2 %s --flag3 %s", value1, value2, value3)) |
// DOs:
// 1- Break the command to name and []args as exec.Command(name, []arg...)
// 2- Use strings.TrimSpace() to build an user defined flags; e.g: fmt.Sprintf("--%s", strings.TrimSpace(userDefinedFlag))
// 3- In case a single arg contains multiple user defined inputs, then use fmt.Sprintf() with %q to sanitize user defined inputs; exec.Command(name, "--flag=", fmt.Sprintf("key1=%q,key2=%q,key3=%q", userDefinedArg-1, userDefinedArg-2, userDefinedArg-2))
func (impl *GitUtil) Checkout(rootDir string, checkout string) (response, errMsg string, err error) {
log.Println(util.DEVTRON, "git checkout ", "location", rootDir)
cmd := exec.Command("git", "-C", rootDir, "checkout", checkout, "--force")
Expand All @@ -36,11 +50,18 @@ func (impl *GitUtil) Checkout(rootDir string, checkout string) (response, errMsg
return output, "", nil
}

// runCommandWithCred uses CLI to run git command and it is prone to script injection |
// Don'ts:
// 1- Never concatenate the whole cmd args into a single string and pass it as exec.Command(name, fmt.Sprintf("--flag1 %s --flag2 %s --flag3 %s", value1, value2, value3)) |
// DOs:
// 1- Break the command to name and []args as exec.Command(name, []arg...)
// 2- Use strings.TrimSpace() to build an user defined flags; e.g: fmt.Sprintf("--%s", strings.TrimSpace(userDefinedFlag))
// 3- In case a single arg contains multiple user defined inputs, then use fmt.Sprintf() with %q to sanitize user defined inputs; exec.Command(name, "--flag=", fmt.Sprintf("key1=%q,key2=%q,key3=%q", userDefinedArg-1, userDefinedArg-2, userDefinedArg-2))
func (impl *GitUtil) runCommandWithCred(cmd *exec.Cmd, userName, password string) (response, errMsg string, err error) {
cmd.Env = append(os.Environ(),
fmt.Sprintf("GIT_ASKPASS=%s", GIT_AKS_PASS),
fmt.Sprintf("GIT_USERNAME=%s", userName), // ignored
fmt.Sprintf("GIT_PASSWORD=%s", password), // this value is used
fmt.Sprintf("GIT_USERNAME=%q", userName), // ignored; %q is used intentionally to sanitise the username
fmt.Sprintf("GIT_PASSWORD=%q", password), // this value is used; %q is used intentionally to sanitise the password
)
return impl.runCommand(cmd)
}
Expand Down Expand Up @@ -99,16 +120,30 @@ func (impl *GitUtil) Clone(gitContext GitContext, rootDir string, remoteUrl stri
return response, errMsg, err
}

// setting user.name and user.email as for non-fast-forward merge, git ask for user.name and email
// Merge sets user.name and user.email as for non-fast-forward merge, git ask for user.name and email |
// Merge uses CLI to run git command and it is prone to script injection |
// Don'ts:
// 1- Never concatenate the whole cmd args into a single string and pass it as exec.Command(name, fmt.Sprintf("--flag1 %s --flag2 %s --flag3 %s", value1, value2, value3)) |
// DOs:
// 1- Break the command to name and []args as exec.Command(name, []arg...)
// 2- Use strings.TrimSpace() to build an user defined flags; e.g: fmt.Sprintf("--%s", strings.TrimSpace(userDefinedFlag))
// 3- In case a single arg contains multiple user defined inputs, then use fmt.Sprintf() with %q to sanitize user defined inputs; exec.Command(name, "--flag=", fmt.Sprintf("key1=%q,key2=%q,key3=%q", userDefinedArg-1, userDefinedArg-2, userDefinedArg-2))
func (impl *GitUtil) Merge(rootDir string, commit string) (response, errMsg string, err error) {
log.Println(util.DEVTRON, "git merge ", "location", rootDir)
command := "cd " + rootDir + " && git config user.email [email protected] && git config user.name Devtron && git merge " + commit + " --no-commit"
command := fmt.Sprintf("cd %q && git config user.email [email protected] && git config user.name Devtron && git merge %q --no-commit", rootDir, commit)
cmd := exec.Command("/bin/sh", "-c", command)
output, errMsg, err := impl.runCommand(cmd)
log.Println(util.DEVTRON, "merge output", "root", rootDir, "opt", output, "errMsg", errMsg, "error", err)
return output, errMsg, err
}

// RecursiveFetchSubmodules uses CLI to run git command and it is prone to script injection |
// Don'ts:
// 1- Never concatenate the whole cmd args into a single string and pass it as exec.Command(name, fmt.Sprintf("--flag1 %s --flag2 %s --flag3 %s", value1, value2, value3)) |
// DOs:
// 1- Break the command to name and []args as exec.Command(name, []arg...)
// 2- Use strings.TrimSpace() to build an user defined flags; e.g: fmt.Sprintf("--%s", strings.TrimSpace(userDefinedFlag))
// 3- In case a single arg contains multiple user defined inputs, then use fmt.Sprintf() with %q to sanitize user defined inputs; exec.Command(name, "--flag=", fmt.Sprintf("key1=%q,key2=%q,key3=%q", userDefinedArg-1, userDefinedArg-2, userDefinedArg-2))
func (impl *GitUtil) RecursiveFetchSubmodules(rootDir string) (response, errMsg string, error error) {
log.Println(util.DEVTRON, "git recursive fetch submodules ", "location", rootDir)
cmd := exec.Command("git", "-C", rootDir, "submodule", "update", "--init", "--recursive")
Expand All @@ -117,6 +152,13 @@ func (impl *GitUtil) RecursiveFetchSubmodules(rootDir string) (response, errMsg
return output, eMsg, err
}

// UpdateCredentialHelper uses CLI to run git command and it is prone to script injection |
// Don'ts:
// 1- Never concatenate the whole cmd args into a single string and pass it as exec.Command(name, fmt.Sprintf("--flag1 %s --flag2 %s --flag3 %s", value1, value2, value3)) |
// DOs:
// 1- Break the command to name and []args as exec.Command(name, []arg...)
// 2- Use strings.TrimSpace() to build an user defined flags; e.g: fmt.Sprintf("--%s", strings.TrimSpace(userDefinedFlag))
// 3- In case a single arg contains multiple user defined inputs, then use fmt.Sprintf() with %q to sanitize user defined inputs; exec.Command(name, "--flag=", fmt.Sprintf("key1=%q,key2=%q,key3=%q", userDefinedArg-1, userDefinedArg-2, userDefinedArg-2))
func (impl *GitUtil) UpdateCredentialHelper(rootDir string) (response, errMsg string, error error) {
log.Println(util.DEVTRON, "git credential helper store ", "location", rootDir)
cmd := exec.Command("git", "-C", rootDir, "config", "--global", "credential.helper", "store")
Expand All @@ -125,6 +167,13 @@ func (impl *GitUtil) UpdateCredentialHelper(rootDir string) (response, errMsg st
return output, eMsg, err
}

// UnsetCredentialHelper uses CLI to run git command and it is prone to script injection |
// Don'ts:
// 1- Never concatenate the whole cmd args into a single string and pass it as exec.Command(name, fmt.Sprintf("--flag1 %s --flag2 %s --flag3 %s", value1, value2, value3)) |
// DOs:
// 1- Break the command to name and []args as exec.Command(name, []arg...)
// 2- Use strings.TrimSpace() to build an user defined flags; e.g: fmt.Sprintf("--%s", strings.TrimSpace(userDefinedFlag))
// 3- In case a single arg contains multiple user defined inputs, then use fmt.Sprintf() with %q to sanitize user defined inputs; exec.Command(name, "--flag=", fmt.Sprintf("key1=%q,key2=%q,key3=%q", userDefinedArg-1, userDefinedArg-2, userDefinedArg-2))
func (impl *GitUtil) UnsetCredentialHelper(rootDir string) (response, errMsg string, error error) {
log.Println(util.DEVTRON, "git credential helper unset ", "location", rootDir)
cmd := exec.Command("git", "-C", rootDir, "config", "--global", "--unset", "credential.helper")
Expand Down