| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -130,6 +130,8 @@ class IndexFile(LazyMixin, git_diff.Diffable, Serializable): | |||
| 130 | 130 | index directly before operating on it using the git command. | |
| 131 | 131 | """ | |
| 132 | 132 | ||
| 133 | + unsafe_git_checkout_index_options = ["--prefix"] | ||
| 134 | + | ||
| 133 | 135 | __slots__ = ("repo", "version", "entries", "_extension_data", "_file_path") | |
| 134 | 136 | ||
| 135 | 137 | _VERSION = 2 | |
@@ -1212,6 +1214,7 @@ def checkout( | |||
| 1212 | 1214 | paths: Union[None, Iterable[PathLike]] = None, | |
| 1213 | 1215 | force: bool = False, | |
| 1214 | 1216 | fprogress: Callable = lambda *args: None, | |
| 1217 | + allow_unsafe_options: bool = False, | ||
| 1215 | 1218 | **kwargs: Any, | |
| 1216 | 1219 | ) -> Union[None, Iterator[PathLike], Sequence[PathLike]]: | |
| 1217 | 1220 | """Check out the given paths or all files from the version known to the index | |
@@ -1238,6 +1241,9 @@ def checkout( | |||
| 1238 | 1241 | no explicit paths are given. Otherwise progress information will be send | |
| 1239 | 1242 | prior and after a file has been checked out. | |
| 1240 | 1243 | ||
| 1244 | + :param allow_unsafe_options: | ||
| 1245 | + Allow unsafe options, such as ``--prefix``. | ||
| 1246 | + | ||
| 1241 | 1247 | :param kwargs: | |
| 1242 | 1248 | Additional arguments to be passed to :manpage:`git-checkout-index(1)`. | |
| 1243 | 1249 | ||
@@ -1261,6 +1267,12 @@ def checkout( | |||
| 1261 | 1267 | i.e. if you want :manpage:`git-checkout(1)`-like behaviour, use | |
| 1262 | 1268 | ``head.checkout`` instead of ``index.checkout``. | |
| 1263 | 1269 | """ | |
| 1270 | + if not allow_unsafe_options: | ||
| 1271 | + Git.check_unsafe_options( | ||
| 1272 | + options=Git._option_candidates([], kwargs), | ||
| 1273 | + unsafe_options=self.unsafe_git_checkout_index_options, | ||
| 1274 | + ) | ||
| 1275 | + | ||
| 1264 | 1276 | args = ["--index"] | |
| 1265 | 1277 | if force: | |
| 1266 | 1278 | args.append("--force") | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -16,6 +16,7 @@ | |||
| 16 | 16 | ||
| 17 | 17 | from typing import Any, TYPE_CHECKING, Type, Union | |
| 18 | 18 | ||
| 19 | + from git.cmd import Git | ||
| 19 | 20 | from git.types import AnyGitObject, PathLike | |
| 20 | 21 | ||
| 21 | 22 | if TYPE_CHECKING: | |
@@ -42,6 +43,8 @@ class TagReference(Reference): | |||
| 42 | 43 | ||
| 43 | 44 | __slots__ = () | |
| 44 | 45 | ||
| 46 | + unsafe_git_tag_options = ["--file", "-F"] | ||
| 47 | + | ||
| 45 | 48 | _common_default = "tags" | |
| 46 | 49 | _common_path_default = Reference._common_path_default + "/" + _common_default | |
| 47 | 50 | ||
@@ -92,6 +95,7 @@ def create( | |||
| 92 | 95 | reference: Union[str, "SymbolicReference"] = "HEAD", | |
| 93 | 96 | logmsg: Union[str, None] = None, | |
| 94 | 97 | force: bool = False, | |
| 98 | + allow_unsafe_options: bool = False, | ||
| 95 | 99 | **kwargs: Any, | |
| 96 | 100 | ) -> "TagReference": | |
| 97 | 101 | """Create a new tag reference. | |
@@ -121,12 +125,21 @@ def create( | |||
| 121 | 125 | :param force: | |
| 122 | 126 | If ``True``, force creation of a tag even though that tag already exists. | |
| 123 | 127 | ||
| 128 | + :param allow_unsafe_options: | ||
| 129 | + Allow unsafe options, such as ``--file``. | ||
| 130 | + | ||
| 124 | 131 | :param kwargs: | |
| 125 | 132 | Additional keyword arguments to be passed to :manpage:`git-tag(1)`. | |
| 126 | 133 | ||
| 127 | 134 | :return: | |
| 128 | 135 | A new :class:`TagReference`. | |
| 129 | 136 | """ | |
| 137 | + if not allow_unsafe_options: | ||
| 138 | + Git.check_unsafe_options( | ||
| 139 | + options=Git._option_candidates([], kwargs), | ||
| 140 | + unsafe_options=cls.unsafe_git_tag_options, | ||
| 141 | + ) | ||
| 142 | + | ||
| 130 | 143 | if "ref" in kwargs and kwargs["ref"]: | |
| 131 | 144 | reference = kwargs["ref"] | |
| 132 | 145 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -151,8 +151,10 @@ class Repo: | |||
| 151 | 151 | "-c", | |
| 152 | 152 | # Can install hooks that execute during clone: | |
| 153 | 153 | "--template", | |
| 154 | + # Fetches from an additional caller-controlled URI: | ||
| 155 | + "--bundle-uri", | ||
| 154 | 156 | ] | |
| 155 | - """Options to :manpage:`git-clone(1)` that allow arbitrary commands to be executed. | ||
| 157 | + """Options to :manpage:`git-clone(1)` that permit unsafe command execution or I/O. | ||
| 156 | 158 | ||
| 157 | 159 | The ``--upload-pack``/``-u`` option allows users to execute arbitrary commands | |
| 158 | 160 | directly: | |
@@ -164,6 +166,11 @@ class Repo: | |||
| 164 | 166 | ||
| 165 | 167 | The ``--template`` option can install hooks that execute during clone: | |
| 166 | 168 | https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---templatetemplate-directory | |
| 169 | + | ||
| 170 | + The ``--bundle-uri`` option fetches from an additional URI before fetching from the | ||
| 171 | + clone URL. An untrusted value can therefore make Git access local files or | ||
| 172 | + unintended network resources: | ||
| 173 | + https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---bundle-uriuri | ||
| 167 | 174 | """ | |
| 168 | 175 | ||
| 169 | 176 | unsafe_git_archive_options = [ | |
@@ -172,6 +179,10 @@ class Repo: | |||
| 172 | 179 | # Writes output to a caller-controlled filesystem path. | |
| 173 | 180 | "--output", | |
| 174 | 181 | "-o", | |
| 182 | + # Reads from a caller-controlled filesystem path: | ||
| 183 | + "--add-file", | ||
| 184 | + # Injects a caller-controlled path and contents: | ||
| 185 | + "--add-virtual-file", | ||
| 175 | 186 | ] | |
| 176 | 187 | ||
| 177 | 188 | unsafe_git_revision_options = [ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -132,6 +132,7 @@ def test_clone_unsafe_options(self, rw_repo): | |||
| 132 | 132 | "-cprotocol.ext.allow=always", | |
| 133 | 133 | "-vcprotocol.ext.allow=always", | |
| 134 | 134 | f"--template={tmp_dir}", | |
| 135 | + f"--bundle-uri=file://{tmp_dir}", | ||
| 135 | 136 | ] | |
| 136 | 137 | for unsafe_option in unsafe_options: | |
| 137 | 138 | with self.assertRaises(UnsafeOptionError): | |
@@ -147,6 +148,7 @@ def test_clone_unsafe_options(self, rw_repo): | |||
| 147 | 148 | {"conf": "protocol.ext.allow=always"}, | |
| 148 | 149 | {"c": "protocol.ext.allow=always"}, | |
| 149 | 150 | {"template": tmp_dir}, | |
| 151 | + {"bundle_uri": f"file://{tmp_dir}"}, | ||
| 150 | 152 | ] | |
| 151 | 153 | for unsafe_option in unsafe_options: | |
| 152 | 154 | with self.assertRaises(UnsafeOptionError): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -31,6 +31,7 @@ | |||
| 31 | 31 | HookExecutionError, | |
| 32 | 32 | InvalidGitRepositoryError, | |
| 33 | 33 | UnmergedEntriesError, | |
| 34 | + UnsafeOptionError, | ||
| 34 | 35 | ) | |
| 35 | 36 | from git.index.fun import hook_path, run_commit_hook | |
| 36 | 37 | from git.index.typ import BaseIndexEntry, IndexEntry | |
@@ -202,6 +203,15 @@ def _make_hook(git_dir, name, content, make_exec=True): | |||
| 202 | 203 | ||
| 203 | 204 | @ddt.ddt | |
| 204 | 205 | class TestIndex(TestBase): | |
| 206 | + @with_rw_repo("HEAD") | ||
| 207 | + def test_checkout_rejects_unsafe_prefix(self, rw_repo): | ||
| 208 | + with tempfile.TemporaryDirectory() as target: | ||
| 209 | + with self.assertRaises(UnsafeOptionError): | ||
| 210 | + rw_repo.index.checkout(prefix=f"{target}/") | ||
| 211 | + | ||
| 212 | + rw_repo.index.checkout(prefix=f"{target}/", allow_unsafe_options=True) | ||
| 213 | + self.assertTrue(osp.isfile(osp.join(target, "CHANGES"))) | ||
| 214 | + | ||
| 205 | 215 | def __init__(self, *args): | |
| 206 | 216 | super().__init__(*args) | |
| 207 | 217 | self._reset_progress() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -23,6 +23,7 @@ | |||
| 23 | 23 | SymbolicReference, | |
| 24 | 24 | TagReference, | |
| 25 | 25 | ) | |
| 26 | + from git.exc import UnsafeOptionError | ||
| 26 | 27 | from git.objects.tag import TagObject | |
| 27 | 28 | import git.refs as refs | |
| 28 | 29 | from git.util import Actor | |
@@ -60,6 +61,18 @@ def test_from_path(self): | |||
| 60 | 61 | # Check remoteness | |
| 61 | 62 | assert Reference(self.rorepo, "refs/remotes/origin").is_remote() | |
| 62 | 63 | ||
| 64 | + @with_rw_repo("HEAD") | ||
| 65 | + def test_tag_create_rejects_unsafe_file_options(self, rw_repo): | ||
| 66 | + with tempfile.NamedTemporaryFile("w", encoding="utf-8") as message: | ||
| 67 | + message.write("private tag message") | ||
| 68 | + message.flush() | ||
| 69 | + for index, option in enumerate(({"F": message.name}, {"file": message.name})): | ||
| 70 | + with self.assertRaises(UnsafeOptionError): | ||
| 71 | + TagReference.create(rw_repo, f"unsafe-{index}", **option) | ||
| 72 | + | ||
| 73 | + tag = TagReference.create(rw_repo, "allowed-file", F=message.name, allow_unsafe_options=True) | ||
| 74 | + self.assertEqual(tag.tag.message, "private tag message") | ||
| 75 | + | ||
| 63 | 76 | def test_from_pathlike(self): | |
| 64 | 77 | # Should be able to create any reference directly. | |
| 65 | 78 | for ref_type in (Reference, Head, TagReference, RemoteReference): | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -433,6 +433,10 @@ def test_archive_rejects_unsafe_options(self): | |||
| 433 | 433 | with self.assertRaises(UnsafeOptionError): | |
| 434 | 434 | self.rorepo.archive(io.BytesIO(), "0.1.6", output=output_marker) | |
| 435 | 435 | assert not osp.exists(output_marker) | |
| 436 | + with self.assertRaises(UnsafeOptionError): | ||
| 437 | + self.rorepo.archive(io.BytesIO(), "0.1.6", add_file=output_marker) | ||
| 438 | + with self.assertRaises(UnsafeOptionError): | ||
| 439 | + self.rorepo.archive(io.BytesIO(), "0.1.6", add_virtual_file="file:content") | ||
| 436 | 440 | ||
| 437 | 441 | def test_archive_rejects_unsafe_remote_protocol(self): | |
| 438 | 442 | with tempfile.TemporaryDirectory() as tdir: | |
| Back | FazBrowse Home | New Git URL |
0 commit comments