GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — fix: validate object types when loading binary SHAs (#2254) · gitpython-developers/GitPython@0610404

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • .basedpyright
    • baseline.json
  • git
    • diff.py
    • index
      • base.py
    • objects
      • base.py
      • commit.py
      • fun.py
      • tag.py
      • tree.py
      • util.py
  • test
    • test_base.py
    • test_commit.py
    • test_diff.py
    • test_index.py
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎.basedpyright/baseline.json‎

Copy file name to clipboardExpand all lines: .basedpyright/baseline.json
-8Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,14 +77,6 @@
7777
"lineCount": 1
7878
}
7979
},
80-
{
81-
"code": "reportAssignmentType",
82-
"range": {
83-
"startColumn": 38,
84-
"endColumn": 76,
85-
"lineCount": 1
86-
}
87-
},
8880
{
8981
"code": "reportArgumentType",
9082
"range": {
Collapse file

‎git/diff.py‎

Copy file name to clipboardExpand all lines: git/diff.py
+12-3Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
from git.cmd import Git, handle_process_output
1313
from git.compat import defenc
14+
from git.objects.base import IndexObject
1415
from git.objects.blob import Blob
1516
from git.objects.util import mode_str_to_int
1617
from git.util import finalize_process, hex_to_bin
@@ -35,7 +36,6 @@
3536
if TYPE_CHECKING:
3637
from subprocess import Popen
3738

38-
from git.objects.base import IndexObject
3939
from git.objects.commit import Commit
4040
from git.objects.tree import Tree
4141
from git.repo.base import Repo
@@ -378,6 +378,10 @@ class Diff:
378378
Diffs keep information about the changed blob objects, the file mode, renames,
379379
deletions and new files.
380380
381+
For submodule changes, ``a_blob`` and ``b_blob`` are
382+
:class:`~git.objects.base.IndexObject` instances whose SHAs refer to commits in
383+
the submodule repository.
384+
381385
There are a few cases where ``None`` has to be expected as member variable value:
382386
383387
New File::
@@ -481,17 +485,22 @@ def __init__(
481485
repo = submodule.module()
482486
break
483487

488+
# Gitlinks reference commits; generic index objects preserve their path and mode.
484489
self.a_blob: Union["IndexObject", None]
485490
if a_blob_id is None or a_blob_id == self.NULL_HEX_SHA:
486491
self.a_blob = None
487492
else:
488-
self.a_blob = Blob(repo, hex_to_bin(a_blob_id), mode=self.a_mode, path=self.a_path)
493+
self.a_blob = (IndexObject if self.a_mode == 0o160000 else Blob)(
494+
repo, hex_to_bin(a_blob_id), mode=self.a_mode, path=self.a_path
495+
)
489496

490497
self.b_blob: Union["IndexObject", None]
491498
if b_blob_id is None or b_blob_id == self.NULL_HEX_SHA:
492499
self.b_blob = None
493500
else:
494-
self.b_blob = Blob(repo, hex_to_bin(b_blob_id), mode=self.b_mode, path=self.b_path)
501+
self.b_blob = (IndexObject if self.b_mode == 0o160000 else Blob)(
502+
repo, hex_to_bin(b_blob_id), mode=self.b_mode, path=self.b_path
503+
)
495504

496505
self.new_file: bool = new_file
497506
self.deleted_file: bool = deleted_file
Collapse file

‎git/index/base.py‎

Copy file name to clipboardExpand all lines: git/index/base.py
+4-2Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ def merge_tree(
310310
return self
311311

312312
@classmethod
313-
def new(cls, repo: "Repo", *tree_sha: Union[str, Tree]) -> "IndexFile":
313+
def new(cls, repo: "Repo", *tree_sha: Union[str, bytes, Tree]) -> "IndexFile":
314314
"""Merge the given treeish revisions into a new index which is returned.
315315
316316
This method behaves like ``git-read-tree --aggressive`` when doing the merge.
@@ -326,7 +326,9 @@ def new(cls, repo: "Repo", *tree_sha: Union[str, Tree]) -> "IndexFile":
326326
If you intend to write such a merged Index, supply an alternate
327327
``file_path`` to its :meth:`write` method.
328328
"""
329-
tree_sha_bytes: List[bytes] = [to_bin_sha(str(t)) for t in tree_sha]
329+
tree_sha_bytes: List[bytes] = [
330+
to_bin_sha(t if isinstance(t, bytes) else str(t).encode("ascii")) for t in tree_sha
331+
]
330332
base_entries = aggressive_tree_merge(repo.odb, tree_sha_bytes)
331333

332334
inst = cls(repo)
Collapse file

‎git/objects/base.py‎

Copy file name to clipboardExpand all lines: git/objects/base.py
+10-3Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from git.exc import WorkTreeRepositoryUnsupported
1313
from git.util import LazyMixin, bin_to_hex, join_path_native, stream_copy
1414

15-
from .util import get_object_type_by_name
15+
from .util import _assert_object_type, get_object_type_by_name
1616

1717
# typing ------------------------------------------------------------------
1818

@@ -107,6 +107,10 @@ def __init__(self, repo: "Repo", binsha: bytes) -> None:
107107
108108
:param binsha:
109109
20 byte SHA1
110+
111+
:note:
112+
Object data is loaded lazily. If `binsha` refers to a different object
113+
type, accessing that data raises :exc:`ValueError`.
110114
"""
111115
super().__init__()
112116
self.repo = repo
@@ -155,6 +159,7 @@ def _set_cache_(self, attr: str) -> None:
155159
"""Retrieve object information."""
156160
if attr == "size":
157161
oinfo = self.repo.odb.info(self.binsha)
162+
_assert_object_type(oinfo, self.type)
158163
self.size = oinfo.size # type: int
159164
else:
160165
super()._set_cache_(attr)
@@ -198,7 +203,9 @@ def data_stream(self) -> "OStream":
198203
:note:
199204
Returned streams must be read in order.
200205
"""
201-
return self.repo.odb.stream(self.binsha)
206+
ostream = self.repo.odb.stream(self.binsha)
207+
_assert_object_type(ostream, self.type)
208+
return ostream
202209

203210
def stream_data(self, ostream: SupportsWrite[bytes]) -> "Object":
204211
"""Write our data directly to the given output stream.
@@ -209,7 +216,7 @@ def stream_data(self, ostream: SupportsWrite[bytes]) -> "Object":
209216
:return:
210217
self
211218
"""
212-
istream = self.repo.odb.stream(self.binsha)
219+
istream = self.data_stream
213220
stream_copy(istream, ostream)
214221
return self
215222

Collapse file

‎git/objects/commit.py‎

Copy file name to clipboardExpand all lines: git/objects/commit.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ def replace(self, **kwargs: Any) -> "Commit":
242242
def _set_cache_(self, attr: str) -> None:
243243
if attr in Commit.__slots__:
244244
# Read the data in a chunk, its faster - then provide a file wrapper.
245-
_binsha, _typename, self.size, stream = self.repo.odb.stream(self.binsha)
245+
_binsha, _typename, self.size, stream = self.data_stream
246246
self._deserialize(BytesIO(stream.read()))
247247
else:
248248
super()._set_cache_(attr)
Collapse file

‎git/objects/fun.py‎

Copy file name to clipboardExpand all lines: git/objects/fun.py
+8-2Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
from git.compat import safe_decode, defenc
1616

17+
from .util import _assert_object_type
18+
1719
# typing ----------------------------------------------
1820

1921
from typing import (
@@ -200,8 +202,10 @@ def traverse_trees_recursive(
200202
if tree_sha is None:
201203
data: List[EntryTupOrNone] = []
202204
else:
205+
stream = odb.stream(tree_sha)
206+
_assert_object_type(stream, "tree")
203207
# Make new list for typing as list invariant.
204-
data = list(tree_entries_from_data(odb.stream(tree_sha).read()))
208+
data = list(tree_entries_from_data(stream.read()))
205209
# END handle muted trees
206210
trees_data.append(data)
207211
# END for each sha to get data for
@@ -268,7 +272,9 @@ def traverse_tree_recursive(odb: "ObjectDBR", tree_sha: bytes, path_prefix: str)
268272
Prefix to prepend to the front of all returned paths.
269273
"""
270274
entries = []
271-
data = tree_entries_from_data(odb.stream(tree_sha).read())
275+
stream = odb.stream(tree_sha)
276+
_assert_object_type(stream, "tree")
277+
data = tree_entries_from_data(stream.read())
272278

273279
# Unpacking/packing is faster than accessing individual items.
274280
for sha, mode, name in data:
Collapse file

‎git/objects/tag.py‎

Copy file name to clipboardExpand all lines: git/objects/tag.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ def __init__(
105105
def _set_cache_(self, attr: str) -> None:
106106
"""Cache all our attributes at once."""
107107
if attr in TagObject.__slots__:
108-
ostream = self.repo.odb.stream(self.binsha)
108+
ostream = self.data_stream
109109
lines: List[str] = ostream.read().decode(defenc, "replace").splitlines()
110110

111111
_obj, hexsha = lines[0].split(" ")
Collapse file

‎git/objects/tree.py‎

Copy file name to clipboardExpand all lines: git/objects/tree.py
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ def _get_intermediate_items(
208208
def _set_cache_(self, attr: str) -> None:
209209
if attr == "_cache":
210210
# Set the data when we need it.
211-
ostream = self.repo.odb.stream(self.binsha)
211+
ostream = self.data_stream
212212
self._cache: List[TreeCacheTup] = tree_entries_from_data(ostream.read())
213213
else:
214214
super()._set_cache_(attr)
Collapse file

‎git/objects/util.py‎

Copy file name to clipboardExpand all lines: git/objects/util.py
+11Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@
5454
from io import BytesIO, StringIO
5555
from subprocess import Popen
5656

57+
from gitdb.base import OInfo
58+
5759
from git.types import Protocol, runtime_checkable
5860

5961
from .blob import Blob
@@ -88,6 +90,15 @@ class TraverseNT(NamedTuple):
8890
# { Functions
8991

9092

93+
def _assert_object_type(oinfo: "OInfo", expected_type: Union[str, None]) -> None:
94+
"""Reject mismatched object types using only their metadata."""
95+
if expected_type is None or oinfo.type == expected_type.encode("ascii"):
96+
return
97+
raise ValueError(
98+
"Object %s is a %s, not a %s" % (oinfo.hexsha.decode("ascii"), oinfo.type.decode("ascii"), expected_type)
99+
)
100+
101+
91102
def mode_str_to_int(modestr: Union[bytes, str]) -> int:
92103
"""Convert mode bits from an octal mode string to an integer mode for git.
93104
Collapse file

‎test/test_base.py‎

Copy file name to clipboardExpand all lines: test/test_base.py
+16-1Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
# 3-Clause BSD License: https://opensource.org/license/bsd-3-clause/
55

66
import gc
7+
from io import BytesIO
78
import os
89
import os.path as osp
910
import sys
@@ -13,7 +14,7 @@
1314
from git import Repo
1415
from git.objects import Blob, Commit, TagObject, Tree
1516
import git.objects.base as base
16-
from git.objects.util import get_object_type_by_name
17+
from git.objects.util import _assert_object_type, get_object_type_by_name
1718
from git.util import HIDE_WINDOWS_FREEZE_ERRORS, hex_to_bin
1819

1920
from test.lib import TestBase as _TestBase, with_rw_and_rw_remote_repo, with_rw_repo
@@ -76,6 +77,20 @@ def test_base_object(self):
7677
# Remove the file this way, instead of with a context manager or "finally",
7778
# so it is only removed on success, and we can inspect the file on failure.
7879
os.remove(tmpfile.name)
80+
81+
for wrong_type, attr in ((Blob, "data_stream"), (Tree, "_cache"), (Commit, "tree"), (TagObject, "object")):
82+
if wrong_type is obj_type:
83+
continue
84+
invalid = wrong_type(self.rorepo, binsha)
85+
for invalid_attr in ("size", attr):
86+
with self.assertRaisesRegex(ValueError, f"{hexsha}.*{typename}.*{wrong_type.type}"):
87+
getattr(invalid, invalid_attr)
88+
with self.assertRaises(ValueError):
89+
invalid.stream_data(BytesIO())
90+
data_stream = item.data_stream
91+
with self.assertRaises(ValueError):
92+
_assert_object_type(data_stream, wrong_type.type)
93+
self.assertEqual(data_stream.read(), data)
7994
# END for each object type to create
8095

8196
# Each has a unique sha.

0 commit comments

Comments
0 (0)
You are visitor 01402520 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.