Skip to content

Commit 8a47059

Browse files
jholgitster
authored andcommitted
git-p4: pass command arguments as lists instead of using shell
In the majority of the subprocess calls where shell=True was used, it was only needed to parse command arguments by spaces. In each of these cases, the commands are now being passed in as lists instead of strings. This change aids the comprehensibility of the code. Constucting commands and arguments using strings risks bugs from unsanitized inputs, and the attendant complexity of properly quoting and escaping command arguments. Signed-off-by: Joel Holdsworth <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 3d8a303 commit 8a47059

File tree

1 file changed

+43
-62
lines changed

1 file changed

+43
-62
lines changed

git-p4.py

Lines changed: 43 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -96,10 +96,7 @@ def p4_build_cmd(cmd):
9696
# Provide a way to not pass this option by setting git-p4.retries to 0
9797
real_cmd += ["-r", str(retries)]
9898

99-
if not isinstance(cmd, list):
100-
real_cmd = ' '.join(real_cmd) + ' ' + cmd
101-
else:
102-
real_cmd += cmd
99+
real_cmd += cmd
103100

104101
# now check that we can actually talk to the server
105102
global p4_access_checked
@@ -721,12 +718,7 @@ def isModeExecChanged(src_mode, dst_mode):
721718
def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False,
722719
errors_as_exceptions=False, *k, **kw):
723720

724-
if not isinstance(cmd, list):
725-
cmd = "-G " + cmd
726-
else:
727-
cmd = ["-G"] + cmd
728-
729-
cmd = p4_build_cmd(cmd)
721+
cmd = p4_build_cmd(["-G"] + cmd)
730722
if verbose:
731723
sys.stderr.write("Opening pipe: %s\n" % str(cmd))
732724

@@ -849,7 +841,7 @@ def isValidGitDir(path):
849841
return git_dir(path) != None
850842

851843
def parseRevision(ref):
852-
return read_pipe("git rev-parse %s" % ref, shell=True).strip()
844+
return read_pipe(["git", "rev-parse", ref]).strip()
853845

854846
def branchExists(ref):
855847
rev = read_pipe(["git", "rev-parse", "-q", "--verify", ref],
@@ -955,13 +947,13 @@ def p4BranchesInGit(branchesAreInRemotes=True):
955947

956948
branches = {}
957949

958-
cmdline = "git rev-parse --symbolic "
950+
cmdline = ["git", "rev-parse", "--symbolic"]
959951
if branchesAreInRemotes:
960-
cmdline += "--remotes"
952+
cmdline.append("--remotes")
961953
else:
962-
cmdline += "--branches"
954+
cmdline.append("--branches")
963955

964-
for line in read_pipe_lines(cmdline, shell=True):
956+
for line in read_pipe_lines(cmdline):
965957
line = line.strip()
966958

967959
# only import to p4/
@@ -1024,7 +1016,7 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent
10241016

10251017
originPrefix = "origin/p4/"
10261018

1027-
for line in read_pipe_lines("git rev-parse --symbolic --remotes", shell=True):
1019+
for line in read_pipe_lines(["git", "rev-parse", "--symbolic", "--remotes"]):
10281020
line = line.strip()
10291021
if (not line.startswith(originPrefix)) or line.endswith("HEAD"):
10301022
continue
@@ -1062,8 +1054,7 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent
10621054
remoteHead, ','.join(settings['depot-paths'])))
10631055

10641056
if update:
1065-
system("git update-ref %s %s" % (remoteHead, originHead),
1066-
shell=True)
1057+
system(["git", "update-ref", remoteHead, originHead])
10671058

10681059
def originP4BranchesExist():
10691060
return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
@@ -1177,7 +1168,7 @@ def getClientSpec():
11771168
"""Look at the p4 client spec, create a View() object that contains
11781169
all the mappings, and return it."""
11791170

1180-
specList = p4CmdList("client -o", shell=True)
1171+
specList = p4CmdList(["client", "-o"])
11811172
if len(specList) != 1:
11821173
die('Output from "client -o" is %d lines, expecting 1' %
11831174
len(specList))
@@ -1206,7 +1197,7 @@ def getClientSpec():
12061197
def getClientRoot():
12071198
"""Grab the client directory."""
12081199

1209-
output = p4CmdList("client -o", shell=True)
1200+
output = p4CmdList(["client", "-o"])
12101201
if len(output) != 1:
12111202
die('Output from "client -o" is %d lines, expecting 1' % len(output))
12121203

@@ -1461,7 +1452,7 @@ def p4UserId(self):
14611452
if self.myP4UserId:
14621453
return self.myP4UserId
14631454

1464-
results = p4CmdList("user -o", shell=True)
1455+
results = p4CmdList(["user", "-o"])
14651456
for r in results:
14661457
if 'User' in r:
14671458
self.myP4UserId = r['User']
@@ -1486,7 +1477,7 @@ def getUserMapFromPerforceServer(self):
14861477
self.users = {}
14871478
self.emails = {}
14881479

1489-
for output in p4CmdList("users", shell=True):
1480+
for output in p4CmdList(["users"]):
14901481
if "User" not in output:
14911482
continue
14921483
self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
@@ -1684,7 +1675,7 @@ def __init__(self):
16841675
die("Large file system not supported for git-p4 submit command. Please remove it from config.")
16851676

16861677
def check(self):
1687-
if len(p4CmdList("opened ...", shell=True)) > 0:
1678+
if len(p4CmdList(["opened", "..."])) > 0:
16881679
die("You have files opened with perforce! Close them before starting the sync.")
16891680

16901681
def separate_jobs_from_description(self, message):
@@ -1788,7 +1779,7 @@ def lastP4Changelist(self):
17881779
# then gets used to patch up the username in the change. If the same
17891780
# client spec is being used by multiple processes then this might go
17901781
# wrong.
1791-
results = p4CmdList("client -o", shell=True) # find the current client
1782+
results = p4CmdList(["client", "-o"]) # find the current client
17921783
client = None
17931784
for r in results:
17941785
if 'Client' in r:
@@ -1804,7 +1795,7 @@ def lastP4Changelist(self):
18041795

18051796
def modifyChangelistUser(self, changelist, newUser):
18061797
# fixup the user field of a changelist after it has been submitted.
1807-
changes = p4CmdList("change -o %s" % changelist, shell=True)
1798+
changes = p4CmdList(["change", "-o", changelist])
18081799
if len(changes) != 1:
18091800
die("Bad output from p4 change modifying %s to user %s" %
18101801
(changelist, newUser))
@@ -1815,7 +1806,7 @@ def modifyChangelistUser(self, changelist, newUser):
18151806
# p4 does not understand format version 3 and above
18161807
input = marshal.dumps(c, 2)
18171808

1818-
result = p4CmdList("change -f -i", stdin=input, shell=True)
1809+
result = p4CmdList(["change", "-f", "-i"], stdin=input)
18191810
for r in result:
18201811
if 'code' in r:
18211812
if r['code'] == 'error':
@@ -1921,7 +1912,7 @@ def edit_template(self, template_file):
19211912
if "P4EDITOR" in os.environ and (os.environ.get("P4EDITOR") != ""):
19221913
editor = os.environ.get("P4EDITOR")
19231914
else:
1924-
editor = read_pipe("git var GIT_EDITOR", shell=True).strip()
1915+
editor = read_pipe(["git", "var", "GIT_EDITOR"]).strip()
19251916
system(["sh", "-c", ('%s "$@"' % editor), editor, template_file])
19261917

19271918
# If the file was not saved, prompt to see if this patch should
@@ -1980,8 +1971,7 @@ def applyCommit(self, id):
19801971
(p4User, gitEmail) = self.p4UserForCommit(id)
19811972

19821973
diff = read_pipe_lines(
1983-
"git diff-tree -r %s \"%s^\" \"%s\"" % (self.diffOpts, id, id),
1984-
shell=True)
1974+
["git", "diff-tree", "-r"] + self.diffOpts + ["{}^".format(id), id])
19851975
filesToAdd = set()
19861976
filesToChangeType = set()
19871977
filesToDelete = set()
@@ -2467,30 +2457,30 @@ def run(self, args):
24672457
#
24682458
if self.detectRenames:
24692459
# command-line -M arg
2470-
self.diffOpts = "-M"
2460+
self.diffOpts = ["-M"]
24712461
else:
24722462
# If not explicitly set check the config variable
24732463
detectRenames = gitConfig("git-p4.detectRenames")
24742464

24752465
if detectRenames.lower() == "false" or detectRenames == "":
2476-
self.diffOpts = ""
2466+
self.diffOpts = []
24772467
elif detectRenames.lower() == "true":
2478-
self.diffOpts = "-M"
2468+
self.diffOpts = ["-M"]
24792469
else:
2480-
self.diffOpts = "-M%s" % detectRenames
2470+
self.diffOpts = ["-M{}".format(detectRenames)]
24812471

24822472
# no command-line arg for -C or --find-copies-harder, just
24832473
# config variables
24842474
detectCopies = gitConfig("git-p4.detectCopies")
24852475
if detectCopies.lower() == "false" or detectCopies == "":
24862476
pass
24872477
elif detectCopies.lower() == "true":
2488-
self.diffOpts += " -C"
2478+
self.diffOpts.append("-C")
24892479
else:
2490-
self.diffOpts += " -C%s" % detectCopies
2480+
self.diffOpts.append("-C{}".format(detectCopies))
24912481

24922482
if gitConfigBool("git-p4.detectCopiesHarder"):
2493-
self.diffOpts += " --find-copies-harder"
2483+
self.diffOpts.append("--find-copies-harder")
24942484

24952485
num_shelves = len(self.update_shelve)
24962486
if num_shelves > 0 and num_shelves != len(commits):
@@ -3436,12 +3426,9 @@ def getBranchMapping(self):
34363426
lostAndFoundBranches = set()
34373427

34383428
user = gitConfig("git-p4.branchUser")
3439-
if len(user) > 0:
3440-
command = "branches -u %s" % user
3441-
else:
3442-
command = "branches"
34433429

3444-
for info in p4CmdList(command, shell=True):
3430+
for info in p4CmdList(
3431+
["branches"] + (["-u", user] if len(user) > 0 else [])):
34453432
details = p4Cmd(["branch", "-o", info["branch"]])
34463433
viewIdx = 0
34473434
while "View%s" % viewIdx in details:
@@ -3532,9 +3519,8 @@ def gitCommitByP4Change(self, ref, change):
35323519
while True:
35333520
if self.verbose:
35343521
print("trying: earliest %s latest %s" % (earliestCommit, latestCommit))
3535-
next = read_pipe(
3536-
"git rev-list --bisect %s %s" % (latestCommit, earliestCommit),
3537-
shell=True).strip()
3522+
next = read_pipe(["git", "rev-list", "--bisect",
3523+
latestCommit, earliestCommit]).strip()
35383524
if len(next) == 0:
35393525
if self.verbose:
35403526
print("argh")
@@ -3689,7 +3675,7 @@ def sync_origin_only(self):
36893675
if self.hasOrigin:
36903676
if not self.silent:
36913677
print('Syncing with origin first, using "git fetch origin"')
3692-
system("git fetch origin", shell=True)
3678+
system(["git", "fetch", "origin"])
36933679

36943680
def importHeadRevision(self, revision):
36953681
print("Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch))
@@ -3856,8 +3842,8 @@ def run(self, args):
38563842
if len(self.branch) == 0:
38573843
self.branch = self.refPrefix + "master"
38583844
if gitBranchExists("refs/heads/p4") and self.importIntoRemotes:
3859-
system("git update-ref %s refs/heads/p4" % self.branch, shell=True)
3860-
system("git branch -D p4", shell=True)
3845+
system(["git", "update-ref", self.branch, "refs/heads/p4"])
3846+
system(["git", "branch", "-D", "p4"])
38613847

38623848
# accept either the command-line option, or the configuration variable
38633849
if self.useClientSpec:
@@ -4060,7 +4046,7 @@ def run(self, args):
40604046
# Cleanup temporary branches created during import
40614047
if self.tempBranches != []:
40624048
for branch in self.tempBranches:
4063-
read_pipe("git update-ref -d %s" % branch, shell=True)
4049+
read_pipe(["git", "update-ref", "-d", branch])
40644050
os.rmdir(os.path.join(os.environ.get("GIT_DIR", ".git"), self.tempBranchLocation))
40654051

40664052
# Create a symbolic ref p4/HEAD pointing to p4/<branch> to allow
@@ -4092,7 +4078,7 @@ def run(self, args):
40924078
def rebase(self):
40934079
if os.system("git update-index --refresh") != 0:
40944080
die("Some files in your working directory are modified and different than what is in your index. You can use git update-index <filename> to bring the index up to date or stash away all your changes with git stash.");
4095-
if len(read_pipe("git diff-index HEAD --", shell=True)) > 0:
4081+
if len(read_pipe(["git", "diff-index", "HEAD", "--"])) > 0:
40964082
die("You have uncommitted changes. Please commit them before rebasing or stash them away with git stash.");
40974083

40984084
[upstream, settings] = findUpstreamBranchPoint()
@@ -4103,10 +4089,10 @@ def rebase(self):
41034089
upstream = re.sub("~[0-9]+$", "", upstream)
41044090

41054091
print("Rebasing the current branch onto %s" % upstream)
4106-
oldHead = read_pipe("git rev-parse HEAD", shell=True).strip()
4107-
system("git rebase %s" % upstream, shell=True)
4108-
system("git diff-tree --stat --summary -M %s HEAD --" % oldHead,
4109-
shell=True)
4092+
oldHead = read_pipe(["git", "rev-parse", "HEAD"]).strip()
4093+
system(["git", "rebase", upstream])
4094+
system(["git", "diff-tree", "--stat", "--summary", "-M", oldHead,
4095+
"HEAD", "--"])
41104096
return True
41114097

41124098
class P4Clone(P4Sync):
@@ -4183,7 +4169,7 @@ def run(self, args):
41834169

41844170
# auto-set this variable if invoked with --use-client-spec
41854171
if self.useClientSpec_from_options:
4186-
system("git config --bool git-p4.useclientspec true", shell=True)
4172+
system(["git", "config", "--bool", "git-p4.useclientspec", "true"])
41874173

41884174
return True
41894175

@@ -4314,10 +4300,7 @@ def run(self, args):
43144300
if originP4BranchesExist():
43154301
createOrUpdateBranchesFromOrigin()
43164302

4317-
cmdline = "git rev-parse --symbolic "
4318-
cmdline += " --remotes"
4319-
4320-
for line in read_pipe_lines(cmdline, shell=True):
4303+
for line in read_pipe_lines(["git", "rev-parse", "--symbolic", "--remotes"]):
43214304
line = line.strip()
43224305

43234306
if not line.startswith('p4/') or line == "p4/HEAD":
@@ -4402,11 +4385,9 @@ def main():
44024385
cmd.gitdir = os.path.abspath(".git")
44034386
if not isValidGitDir(cmd.gitdir):
44044387
# "rev-parse --git-dir" without arguments will try $PWD/.git
4405-
cmd.gitdir = read_pipe(
4406-
"git rev-parse --git-dir", shell=True).strip()
4388+
cmd.gitdir = read_pipe(["git", "rev-parse", "--git-dir"]).strip()
44074389
if os.path.exists(cmd.gitdir):
4408-
cdup = read_pipe(
4409-
"git rev-parse --show-cdup", shell=True).strip()
4390+
cdup = read_pipe(["git", "rev-parse", "--show-cdup"]).strip()
44104391
if len(cdup) > 0:
44114392
chdir(cdup);
44124393

0 commit comments

Comments
 (0)