PyPI / dulwich / 1.2.18

dulwich 1.2.18

Source
https://github.com/dulwich/dulwich
Artifact scanned
dulwich-1.2.18-py3-none-any.whl (wheel · 736.9 KB · SHA-256 52f330ffd7d4…)
Compared with
dulwich-1.2.17-py3-none-any.whl (0 signals introduced since)
Files
89 scanned, 5 skipped (binary, oversized, or unrecognised)
Signals
0 critical · 0 warn · 11 info
Scan time
10.9 s
LLM review
Reviewed by claude-haiku-5-5 (Anthropic): 11 of 11 warn and critical signals; 11 demoted to info.
Disclude version
4.3.0
Scanned (UTC)
2026-10-24 09:12

Signals

Ordered by severity, then signals introduced in this release. Hidden characters are shown as ⟨U+…⟩.

  1. info critical → info by review dynamic-execution dulwich/cli.py:952:38

    `subprocess.Popen` with `shell=True` and a non-literal command — runtime-determined shell invocation (ast pass, confidence 0.85)

                    pager_cmd = self._get_pager_command()
                    self.pager_process = subprocess.Popen(
                        pager_cmd,
                        shell=True,
    likely benign LLM, confidence 0.80 Launches the user's configured pager command through a shell for CLI output.
    Reasoning

    The shell=True Popen runs a pager command returned by _get_pager_command, which in git tooling comes from configuration or environment variables like GIT_PAGER/PAGER. That is the documented way git pagers work, and the snippet shows no obfuscation or hidden payload.

  2. info critical → info by review dynamic-import dulwich/cli.py:5301:26

    `__import__` called on a non-literal expression (ast pass, confidence 0.70)

                try:
                    module = __import__(dep)
                    version = getattr(module, "__version__", "(unknown)")
                    logger.info("  %s: %s [%s]", dep, version, dep_type)
    likely benign LLM, confidence 0.80 Imports a named dependency to print its version in a diagnostics command.
    Reasoning

    __import__ is applied to a loop variable over a list of dependency names to read __version__ for a diagnostic log line. The dependency names are a fixed set in the CLI's own code, so this is a version-reporting pattern, not dynamic code loading from untrusted input.

  3. info critical → info by review dynamic-execution dulwich/cli.py:6476:22

    `subprocess.run` with `shell=True` and a non-literal command — runtime-determined shell invocation (ast pass, confidence 0.85)

                    filter_env.update(extra_env)
                result = subprocess.run(
                    cmd,
                    shell=True,
    likely benign LLM, confidence 0.75 Runs a configured filter command through the shell, as git filter drivers do.
    Reasoning

    subprocess.run with shell=True executes a command assembled from the repository's filter configuration, with extra environment variables merged in. Git filter drivers are user-configured shell commands by design. The trust boundary is the local repo config, which is a real but expected risk.

  4. info warn → info by review decoder-decompress-payload dulwich/dumb.py:146:24

    file imports decoder module(s) zlib and calls `.decompress()` — embedded payload decompressed at runtime (ast pass, confidence 0.75)

            # Decompress and parse the object
            decompressed = zlib.decompress(compressed)
    
            # Parse header
    likely benign LLM, confidence 0.90 Decompresses a fetched git object before parsing its header.
    Reasoning

    zlib.decompress is applied to a fetched git loose object, which is zlib-compressed by the git format. Decompression is required to parse objects, so this is ordinary protocol handling rather than a hidden payload.

  5. info critical → info by review dynamic-execution dulwich/filters.py:171:33

    `subprocess.Popen` with `shell=True` and a non-literal command — runtime-determined shell invocation (ast pass, confidence 0.85)

                try:
                    self._process = subprocess.Popen(
                        self.process_cmd,
                        shell=True,
    likely benign LLM, confidence 0.80 Starts a configured long-running filter process via the shell.
    Reasoning

    The Popen call runs self.process_cmd through a shell for a long-running filter process. This matches git's process filter protocol, where the command comes from git config (filter.<name>.process). Config-driven execution is expected, and the snippet shows no concealment.

  6. info critical → info by review dynamic-execution dulwich/merge_drivers.py:139:26

    `subprocess.run` with `shell=True` and a non-literal command — runtime-determined shell invocation (ast pass, confidence 0.85)

                try:
                    result = subprocess.run(
                        cmd,
                        shell=True,
    likely benign LLM, confidence 0.80 Runs a configured custom merge driver command via the shell.
    Reasoning

    subprocess.run with shell=True executes a merge driver command taken from git configuration (merge.<driver>.driver). Git supports this feature, and the command is a user-configured string, not an encoded or hidden payload.

  7. info warn → info by review decoder-decompress-payload dulwich/objects.py:194:13

    file imports decoder module(s) binascii, zlib and calls `.decompress()` — embedded payload decompressed at runtime (ast pass, confidence 0.75)

        """
        dcomp = zlib.decompressobj()
        # +1 so overrun surfaces as unconsumed_tail rather than being truncated.
        dcomped = dcomp.decompress(string, max_size + 1)
    likely benign LLM, confidence 0.95 Bounded zlib decompression of git object data.
    Reasoning

    decompressobj is used with a max_size bound to inflate git object data, with a comment explaining the unconsumed_tail handling. This is standard bounded zlib streaming for git pack and object data.

  8. info warn → info by review decoder-decompress-payload dulwich/pack.py:634:18

    file imports decoder module(s) binascii, zlib and calls `.decompress()` — embedded payload decompressed at runtime (ast pass, confidence 0.75)

            raise ValueError("non-negative zlib data stream size expected")
        decomp_obj = zlib.decompressobj()
    
        comp_chunks = []
    likely benign LLM, confidence 0.95 Decompresses zlib data streams stored in git pack files.
    Reasoning

    decompressobj is used to read zlib streams from pack files, which git stores compressed. The code validates the size argument first, and the decompression is the expected pack reading path.

  9. info warn → info by review decoder-decompress-payload dulwich/patch.py:1754:19

    file imports decoder module(s) base64, zlib and calls `.decompress()` — embedded payload decompressed at runtime (ast pass, confidence 0.75)

        try:
            decoded = zlib.decompress(git_base85_decode(data))
        except (ValueError, zlib.error) as e:
            raise ValueError(f"Failed to decode binary patch: {e}")
    likely benign LLM, confidence 0.95 Decodes and decompresses a git binary patch hunk.
    Reasoning

    zlib.decompress over git_base85_decode output is the standard decoding of git binary patch hunks, and the surrounding code catches decode errors and raises a clear ValueError. It is a normal patch-parsing path.

  10. info critical → info by review dynamic-execution dulwich/rebase.py:1194:21

    `subprocess.run` with `shell=True` and a non-literal command — runtime-determined shell invocation (ast pass, confidence 0.85)

                    try:
                        subprocess.run(entry.arguments, shell=True, check=True)
                    except subprocess.CalledProcessError as e:
                        # Command failed, pause rebase
    likely benign LLM, confidence 0.80 Runs an interactive-rebase exec command through the shell.
    Reasoning

    subprocess.run with shell=True executes an 'exec' entry from the rebase todo list, the same shell command semantics git rebase uses for exec lines. The command comes from the user's own rebase plan, and the code pauses the rebase on failure, which is expected behavior.

  11. info critical → info by review dynamic-execution dulwich/signature.py:1223:26

    `subprocess.run` with `shell=True` and a non-literal command — runtime-determined shell invocation (ast pass, confidence 0.85)

                    # Run the default key command to get the key
                    result = subprocess.run(
                        self.default_key_command,
                        shell=True,
    likely benign LLM, confidence 0.80 Runs a configured command to obtain the default signing key.
    Reasoning

    subprocess.run with shell=True runs a default key command, which is configured for GPG signing (for example, a command that prints the key id). Configured commands are a normal feature, and the snippet shows no concealment or network activity.

Reproduce This Scan

Run disclude locally to verify. These commands scan the published file; the "new in this release" marks come from comparing it with the previous release's file, which they don't repeat. Add --format sarif for SARIF output. Add --llm (with an API key in the environment) to include the LLM review.

cargo install disclude --version 4.3.0
curl -fsSL -o dulwich-1.2.18-py3-none-any.whl https://files.pythonhosted.org/packages/20/a0/1785faf0b6a7a90abc13e4fa9d97979a777d6b0d2b8ce81a8659a3e1add3/dulwich-1.2.18-py3-none-any.whl
printf '%s  %s\n' 52f330ffd7d4996ce9c1d7ebff15054f151ac3eff2f333aadf235e446d0d1e86 dulwich-1.2.18-py3-none-any.whl | shasum -a 256 -c -
unzip -q dulwich-1.2.18-py3-none-any.whl -d pkg
disclude scan --untrusted --severity info pkg