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 <jholdsworth@nvidia.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Joel Holdsworth committed
Jan 6, 2022 at 21:40 UTC
8a470599f340e2e2bcf96f0b267199de321edef2
1 file changed
+43
-62
git-p4.py
+43
-62
@@ -96,10 +96,7 @@ def p4_build_cmd(cmd):
96
# Provide a way to not pass this option by setting git-p4.retries to 0
97
real_cmd += ["-r", str(retries)]
98
99
- if not isinstance(cmd, list):
100
- real_cmd = ' '.join(real_cmd) + ' ' + cmd
101
- else:
102
- real_cmd += cmd
99
+ real_cmd += cmd
100
101
# now check that we can actually talk to the server
102
global p4_access_checked
@@ -721,12 +718,7 @@ def isModeExecChanged(src_mode, dst_mode):
718
def p4CmdList(cmd, stdin=None, stdin_mode='w+b', cb=None, skip_info=False,
719
errors_as_exceptions=False, *k, **kw):
720
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)
722
if verbose:
723
sys.stderr.write("Opening pipe: %s\n" % str(cmd))
724
@@ -849,7 +841,7 @@ def isValidGitDir(path):
841
return git_dir(path) != None
842
843
def parseRevision(ref):
852
- return read_pipe("git rev-parse %s" % ref, shell=True).strip()
844
+ return read_pipe(["git", "rev-parse", ref]).strip()
845
846
def branchExists(ref):
847
rev = read_pipe(["git", "rev-parse", "-q", "--verify", ref],
@@ -955,13 +947,13 @@ def p4BranchesInGit(branchesAreInRemotes=True):
947
948
branches = {}
949
958
- cmdline = "git rev-parse --symbolic "
950
+ cmdline = ["git", "rev-parse", "--symbolic"]
951
if branchesAreInRemotes:
960
- cmdline += "--remotes"
952
+ cmdline.append("--remotes")
953
else:
962
- cmdline += "--branches"
954
+ cmdline.append("--branches")
955
964
- for line in read_pipe_lines(cmdline, shell=True):
956
+ for line in read_pipe_lines(cmdline):
957
line = line.strip()
958
959
# only import to p4/
@@ -1024,7 +1016,7 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent
1016
1017
originPrefix = "origin/p4/"
1018
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"]):
1020
line = line.strip()
1021
if (not line.startswith(originPrefix)) or line.endswith("HEAD"):
1022
continue
@@ -1062,8 +1054,7 @@ def createOrUpdateBranchesFromOrigin(localRefPrefix = "refs/remotes/p4/", silent
1054
remoteHead, ','.join(settings['depot-paths'])))
1055
1056
if update:
1065
- system("git update-ref %s %s" % (remoteHead, originHead),
1066
- shell=True)
1057
+ system(["git", "update-ref", remoteHead, originHead])
1058
1059
def originP4BranchesExist():
1060
return gitBranchExists("origin") or gitBranchExists("origin/p4") or gitBranchExists("origin/p4/master")
@@ -1177,7 +1168,7 @@ def getClientSpec():
1168
"""Look at the p4 client spec, create a View() object that contains
1169
all the mappings, and return it."""
1170
1180
- specList = p4CmdList("client -o", shell=True)
1171
+ specList = p4CmdList(["client", "-o"])
1172
if len(specList) != 1:
1173
die('Output from "client -o" is %d lines, expecting 1' %
1174
len(specList))
@@ -1206,7 +1197,7 @@ def getClientSpec():
1197
def getClientRoot():
1198
"""Grab the client directory."""
1199
1209
- output = p4CmdList("client -o", shell=True)
1200
+ output = p4CmdList(["client", "-o"])
1201
if len(output) != 1:
1202
die('Output from "client -o" is %d lines, expecting 1' % len(output))
1203
@@ -1461,7 +1452,7 @@ class P4UserMap:
1452
if self.myP4UserId:
1453
return self.myP4UserId
1454
1464
- results = p4CmdList("user -o", shell=True)
1455
+ results = p4CmdList(["user", "-o"])
1456
for r in results:
1457
if 'User' in r:
1458
self.myP4UserId = r['User']
@@ -1486,7 +1477,7 @@ class P4UserMap:
1477
self.users = {}
1478
self.emails = {}
1479
1489
- for output in p4CmdList("users", shell=True):
1480
+ for output in p4CmdList(["users"]):
1481
if "User" not in output:
1482
continue
1483
self.users[output["User"]] = output["FullName"] + " <" + output["Email"] + ">"
@@ -1684,7 +1675,7 @@ class P4Submit(Command, P4UserMap):
1675
die("Large file system not supported for git-p4 submit command. Please remove it from config.")
1676
1677
def check(self):
1687
- if len(p4CmdList("opened ...", shell=True)) > 0:
1678
+ if len(p4CmdList(["opened", "..."])) > 0:
1679
die("You have files opened with perforce! Close them before starting the sync.")
1680
1681
def separate_jobs_from_description(self, message):
@@ -1788,7 +1779,7 @@ class P4Submit(Command, P4UserMap):
1779
# then gets used to patch up the username in the change. If the same
1780
# client spec is being used by multiple processes then this might go
1781
# wrong.
1791
- results = p4CmdList("client -o", shell=True) # find the current client
1782
+ results = p4CmdList(["client", "-o"]) # find the current client
1783
client = None
1784
for r in results:
1785
if 'Client' in r:
@@ -1804,7 +1795,7 @@ class P4Submit(Command, P4UserMap):
1795
1796
def modifyChangelistUser(self, changelist, newUser):
1797
# 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])
1799
if len(changes) != 1:
1800
die("Bad output from p4 change modifying %s to user %s" %
1801
(changelist, newUser))
@@ -1815,7 +1806,7 @@ class P4Submit(Command, P4UserMap):
1806
# p4 does not understand format version 3 and above
1807
input = marshal.dumps(c, 2)
1808
1818
- result = p4CmdList("change -f -i", stdin=input, shell=True)
1809
+ result = p4CmdList(["change", "-f", "-i"], stdin=input)
1810
for r in result:
1811
if 'code' in r:
1812
if r['code'] == 'error':
@@ -1921,7 +1912,7 @@ class P4Submit(Command, P4UserMap):
1912
if "P4EDITOR" in os.environ and (os.environ.get("P4EDITOR") != ""):
1913
editor = os.environ.get("P4EDITOR")
1914
else:
1924
- editor = read_pipe("git var GIT_EDITOR", shell=True).strip()
1915
+ editor = read_pipe(["git", "var", "GIT_EDITOR"]).strip()
1916
system(["sh", "-c", ('%s "$@"' % editor), editor, template_file])
1917
1918
# If the file was not saved, prompt to see if this patch should
@@ -1980,8 +1971,7 @@ class P4Submit(Command, P4UserMap):
1971
(p4User, gitEmail) = self.p4UserForCommit(id)
1972
1973
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])
1975
filesToAdd = set()
1976
filesToChangeType = set()
1977
filesToDelete = set()
@@ -2467,17 +2457,17 @@ class P4Submit(Command, P4UserMap):
2457
#
2458
if self.detectRenames:
2459
# command-line -M arg
2470
- self.diffOpts = "-M"
2460
+ self.diffOpts = ["-M"]
2461
else:
2462
# If not explicitly set check the config variable
2463
detectRenames = gitConfig("git-p4.detectRenames")
2464
2465
if detectRenames.lower() == "false" or detectRenames == "":
2476
- self.diffOpts = ""
2466
+ self.diffOpts = []
2467
elif detectRenames.lower() == "true":
2478
- self.diffOpts = "-M"
2468
+ self.diffOpts = ["-M"]
2469
else:
2480
- self.diffOpts = "-M%s" % detectRenames
2470
+ self.diffOpts = ["-M{}".format(detectRenames)]
2471
2472
# no command-line arg for -C or --find-copies-harder, just
2473
# config variables
@@ -2485,12 +2475,12 @@ class P4Submit(Command, P4UserMap):
2475
if detectCopies.lower() == "false" or detectCopies == "":
2476
pass
2477
elif detectCopies.lower() == "true":
2488
- self.diffOpts += " -C"
2478
+ self.diffOpts.append("-C")
2479
else:
2490
- self.diffOpts += " -C%s" % detectCopies
2480
+ self.diffOpts.append("-C{}".format(detectCopies))
2481
2482
if gitConfigBool("git-p4.detectCopiesHarder"):
2493
- self.diffOpts += " --find-copies-harder"
2483
+ self.diffOpts.append("--find-copies-harder")
2484
2485
num_shelves = len(self.update_shelve)
2486
if num_shelves > 0 and num_shelves != len(commits):
@@ -3436,12 +3426,9 @@ class P4Sync(Command, P4UserMap):
3426
lostAndFoundBranches = set()
3427
3428
user = gitConfig("git-p4.branchUser")
3439
- if len(user) > 0:
3440
- command = "branches -u %s" % user
3441
- else:
3442
- command = "branches"
3429
3444
- for info in p4CmdList(command, shell=True):
3430
+ for info in p4CmdList(
3431
+ ["branches"] + (["-u", user] if len(user) > 0 else [])):
3432
details = p4Cmd(["branch", "-o", info["branch"]])
3433
viewIdx = 0
3434
while "View%s" % viewIdx in details:
@@ -3532,9 +3519,8 @@ class P4Sync(Command, P4UserMap):
3519
while True:
3520
if self.verbose:
3521
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()
3524
if len(next) == 0:
3525
if self.verbose:
3526
print("argh")
@@ -3689,7 +3675,7 @@ class P4Sync(Command, P4UserMap):
3675
if self.hasOrigin:
3676
if not self.silent:
3677
print('Syncing with origin first, using "git fetch origin"')
3692
- system("git fetch origin", shell=True)
3678
+ system(["git", "fetch", "origin"])
3679
3680
def importHeadRevision(self, revision):
3681
print("Doing initial import of %s from revision %s into %s" % (' '.join(self.depotPaths), revision, self.branch))
@@ -3856,8 +3842,8 @@ class P4Sync(Command, P4UserMap):
3842
if len(self.branch) == 0:
3843
self.branch = self.refPrefix + "master"
3844
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"])
3847
3848
# accept either the command-line option, or the configuration variable
3849
if self.useClientSpec:
@@ -4060,7 +4046,7 @@ class P4Sync(Command, P4UserMap):
4046
# Cleanup temporary branches created during import
4047
if self.tempBranches != []:
4048
for branch in self.tempBranches:
4063
- read_pipe("git update-ref -d %s" % branch, shell=True)
4049
+ read_pipe(["git", "update-ref", "-d", branch])
4050
os.rmdir(os.path.join(os.environ.get("GIT_DIR", ".git"), self.tempBranchLocation))
4051
4052
# Create a symbolic ref p4/HEAD pointing to p4/<branch> to allow
@@ -4092,7 +4078,7 @@ class P4Rebase(Command):
4078
def rebase(self):
4079
if os.system("git update-index --refresh") != 0:
4080
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:
4082
die("You have uncommitted changes. Please commit them before rebasing or stash them away with git stash.");
4083
4084
[upstream, settings] = findUpstreamBranchPoint()
@@ -4103,10 +4089,10 @@ class P4Rebase(Command):
4089
upstream = re.sub("~[0-9]+$", "", upstream)
4090
4091
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", "--"])
4096
return True
4097
4098
class P4Clone(P4Sync):
@@ -4183,7 +4169,7 @@ class P4Clone(P4Sync):
4169
4170
# auto-set this variable if invoked with --use-client-spec
4171
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"])
4173
4174
return True
4175
@@ -4314,10 +4300,7 @@ class P4Branches(Command):
4300
if originP4BranchesExist():
4301
createOrUpdateBranchesFromOrigin()
4302
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"]):
4304
line = line.strip()
4305
4306
if not line.startswith('p4/') or line == "p4/HEAD":
@@ -4402,11 +4385,9 @@ def main():
4385
cmd.gitdir = os.path.abspath(".git")
4386
if not isValidGitDir(cmd.gitdir):
4387
# "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()
4389
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()
4391
if len(cdup) > 0:
4392
chdir(cdup);
4393