Skip to content

Merge main#34

Merged
SequeI merged 8 commits intomainfrom
mergeMain
Jan 23, 2026
Merged

Merge main#34
SequeI merged 8 commits intomainfrom
mergeMain

Conversation

@SequeI
Copy link
Member

@SequeI SequeI commented Jan 23, 2026

User description

Summary

Checklist
  • All commits are signed-off, using DCO
  • All new code has docstrings and type annotations
  • All new code is covered by tests. Aim for at least 90% coverage. CI is configured to highlight lines not covered by tests.
  • Public facing changes are paired with documentation changes
  • Release note has been added to CHANGELOG.md if needed

PR Type

Enhancement, Bug fix


Description

  • Added digest subcommand to compute and output model directory digests

  • Standardized CLI flags to use hyphens instead of underscores with backwards compatibility

  • Fixed null check in signing configuration to use explicit is None comparison

  • Added integration tests for custom trust configuration support

  • Updated GitHub Actions workflows and dependencies


Diagram Walkthrough

flowchart LR
  A["CLI Flags"] -->|"Standardize to hyphens"| B["Token Normalization"]
  C["Digest Subcommand"] -->|"Compute model hash"| D["Output Root Digest"]
  E["Signing Config"] -->|"Fix null check"| F["Use is None"]
  G["Tests"] -->|"Add custom trust config"| H["Integration Tests"]
Loading

File Walkthrough

Relevant files
Enhancement
2 files
_cli.py
Standardize CLI flags and add digest subcommand                   
+71/-23 
lint.yml
Add CLI flag underscore validation workflow                           
+17/-0   
Bug fix
1 files
signing.py
Fix null check comparison in signing config                           
+1/-1     
Tests
3 files
sigstore_test.py
Add custom trust config integration tests                               
+88/-0   
api_test.py
Add sign and verify with custom trust config test               
+46/-0   
custom_trust_config.json
Add custom trust configuration test data file                       
+175/-0 
Dependencies
3 files
codeql.yml
Update CodeQL action to v4.31.10                                                 
+3/-3     
cross_os.yml
Update OIDC beacon dependency version                                       
+1/-1     
scorecard.yml
Update CodeQL action to v4.31.10                                                 
+1/-1     
Documentation
2 files
CHANGELOG.md
Document digest subcommand and CLI flag changes                   
+2/-0     
README.md
Update documentation with digest subcommand and flag examples
+17/-8   

dependabot bot and others added 8 commits January 12, 2026 05:34
…c-beacon (sigstore#598)

Bumps the all group with 1 update: [sigstore-conformance/extremely-dangerous-public-oidc-beacon](https://github.com/sigstore-conformance/extremely-dangerous-public-oidc-beacon).


Updates `sigstore-conformance/extremely-dangerous-public-oidc-beacon` from 9775b7374737339e046064d8e5a4bbf4b00565a4 to 1e3cabecd3790f48b79a795424e12fa3cb880dcb
- [Commits](sigstore-conformance/extremely-dangerous-public-oidc-beacon@9775b73...1e3cabe)

---
updated-dependencies:
- dependency-name: sigstore-conformance/extremely-dangerous-public-oidc-beacon
  dependency-version: 1e3cabecd3790f48b79a795424e12fa3cb880dcb
  dependency-type: direct:production
  dependency-group: all
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Add digest subcommand

Signed-off-by: Adolfo García Veytia (Puerco) <adolfo.garcia@uservers.net>

* Update README with digest subcommand info

Signed-off-by: Adolfo García Veytia (Puerco) <adolfo.garcia@uservers.net>

* Add digest entry to changelog

Signed-off-by: Adolfo García Veytia (Puerco) <adolfo.garcia@uservers.net>

---------

Signed-off-by: Adolfo García Veytia (Puerco) <adolfo.garcia@uservers.net>
* ci/cd: adding tests for custom trust config

Signed-off-by: SequeI <asiek@redhat.com>

* fix: post-review changes

Signed-off-by: SequeI <asiek@redhat.com>

---------

Signed-off-by: SequeI <asiek@redhat.com>
Use hyphens instead of underscores for all CLI flags (e.g., --trust-config
instead of --trust_config). Underscore variants still accepted via
token_normalize_func for backwards compatibility.

Signed-off-by: SequeI <asiek@redhat.com>
This is a follow up of 6a49086, with the intention of preventing new
flags from being introduced which violate our now unified style.

Signed-off-by: Spencer Schrock <sschrock@google.com>
Bumps the all group with 1 update: [github/codeql-action](https://github.com/github/codeql-action).


Updates `github/codeql-action` from 4.31.9 to 4.31.10
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](github/codeql-action@5d4e8d1...cdefb33)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.31.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Brings in upstream changes:
- Digest subcommand for computing model digests
- CLI flag standardization (hyphens instead of underscores)
- Tests for custom trust config
- Dependency updates

Resolved conflicts:
- README.md: Keep securesign image URL
- CHANGELOG.md: Merged upstream changes with RH-specific entries
@qodo-code-review
Copy link

ⓘ Your approaching your monthly quota for Qodo. Upgrade your plan

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Untrusted CI action

Description: The workflow invokes the third-party GitHub Action
sigstore-conformance/extremely-dangerous-public-oidc-beacon@1e3cabecd3790f48b79a795424e12fa3cb880dcb,
which runs untrusted external code in CI and can access the job’s OIDC token and workspace
(e.g., write/exfiltrate oidc-token.txt), so its use should be explicitly justified and
tightly permission-scoped.
cross_os.yml [76-81]

Referred Code
  uses: pypa/hatch@257e27e51a6a5616ed08a39a408a21c35c9931bc # install
- name: store beacon token into oidc-token.txt
  uses: sigstore-conformance/extremely-dangerous-public-oidc-beacon@1e3cabecd3790f48b79a795424e12fa3cb880dcb # main
- name: Sign the model
  run: hatch run python -m model_signing sign sigstore model_root/ --use_staging --signature model.sig --identity_token $(cat oidc-token.txt)
- name: upload model signature
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Raw exception exposed: The digest subcommand prints the raw exception message to stderr
(click.echo(f"Computing digest failed: {err}", err=True)), potentially exposing
internal details to end users instead of returning a generic user-facing error and logging
detailed diagnostics internally.

Referred Code
except Exception as err:
    click.echo(f"Computing digest failed: {err}", err=True)
    sys.exit(1)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status:
Missing audit logging: The new digest subcommand performs a potentially security-relevant filesystem operation
(hashing a model directory) but does not emit structured audit logs with actor/context and
outcome, making it unclear whether audit trail requirements apply to this CLI action.

Referred Code
@main.command(name="digest")
@_model_path_argument
@_ignore_paths_option
@_ignore_git_paths_option
@_allow_symlinks_option
def _digest(
    model_path: pathlib.Path,
    ignore_paths: Iterable[pathlib.Path],
    ignore_git_paths: bool,
    allow_symlinks: bool,
) -> None:
    """Computes the digest of a model.

    The digest subcommand serializes a model directory and computes the "root"
    digest (hash), the same used when signing and as the attestation subject.

    By default, git-related files are ignored (same behavior as the sign
    command). Use --no-ignore-git-paths to include them. To ignore other
    files from the directory serialization, use --ignore-paths.
    """
    from model_signing._hashing import memory


 ... (clipped 22 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Overbroad exception catch: The new digest command wraps all failures in a blanket except Exception and exits, which
may mask specific actionable error handling for expected edge cases (e.g., missing path,
permission errors, invalid directory) that should be handled explicitly.

Referred Code
try:
    # First, generate the manifest of the model directory
    ignored = _resolve_ignore_paths(model_path, list(ignore_paths))
    manifest = (
        model_signing.hashing.Config()
        .set_ignored_paths(paths=ignored, ignore_git_paths=ignore_git_paths)
        .set_allow_symlinks(allow_symlinks)
        .hash(model_path)
    )

    # Then, hash the resource descriptors as done when signing
    hasher = memory.SHA256()
    for descriptor in manifest.resource_descriptors():
        hasher.update(descriptor.digest.digest_value)
    root_digest = hasher.compute()

    click.echo(f"{root_digest.algorithm}:{root_digest.digest_hex}")

except Exception as err:
    click.echo(f"Computing digest failed: {err}", err=True)
    sys.exit(1)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Filesystem input handling: The new digest command hashes user-supplied filesystem paths (including optional symlink
following via --allow-symlinks) without visible validation/guardrails in the diff, so it
is unclear whether symlink/path traversal and sensitive file inclusion risks are
sufficiently mitigated elsewhere.

Referred Code
@main.command(name="digest")
@_model_path_argument
@_ignore_paths_option
@_ignore_git_paths_option
@_allow_symlinks_option
def _digest(
    model_path: pathlib.Path,
    ignore_paths: Iterable[pathlib.Path],
    ignore_git_paths: bool,
    allow_symlinks: bool,
) -> None:
    """Computes the digest of a model.

    The digest subcommand serializes a model directory and computes the "root"
    digest (hash), the same used when signing and as the attestation subject.

    By default, git-related files are ignored (same behavior as the sign
    command). Use --no-ignore-git-paths to include them. To ignore other
    files from the directory serialization, use --ignore-paths.
    """
    from model_signing._hashing import memory


 ... (clipped 12 lines)

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link

ⓘ Your approaching your monthly quota for Qodo. Upgrade your plan

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Remove duplicated abstract method definition

Remove the duplicated abstract method definition for serialize in the Serializer
class to resolve a syntax error.

src/model_signing/_serialization/serialization.py [73-111]

 class Serializer(metaclass=abc.ABCMeta):
     """Generic ML model format serializer."""
 
     @abc.abstractmethod
     def serialize(
         self,
         model_path: pathlib.Path,
         *,
         ignore_paths: Iterable[pathlib.Path] = frozenset(),
     ) -> manifest.Manifest:
         """Serializes the model given by the `model_path` argument.
 
         Args:
             model_path: The path to the model.
             ignore_paths: The paths to ignore during serialization. If a
               provided path is a directory, all children of the directory are
               ignored.
 
         Returns:
             The model's serialized manifest.
         """
-    @abc.abstractmethod
-    def serialize(
-        self,
-        model_path: pathlib.Path,
-        *,
-        ignore_paths: Iterable[pathlib.Path] = frozenset(),
-    ) -> manifest.Manifest:
-        """Serializes the model given by the `model_path` argument.
 
-        Args:
-            model_path: The path to the model.
-            ignore_paths: The paths to ignore during serialization. If a
-              provided path is a directory, all children of the directory are
-              ignored.
-
-        Returns:
-            The model's serialized manifest.
-        """
-

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 10

__

Why: The suggestion correctly identifies a duplicated method definition for serialize, which is a SyntaxError and will break the application. This is a critical issue that must be fixed.

High
Remove duplicated method definition

Remove the duplicated verify method definition within the Verifier class to fix
a syntax error.

src/model_signing/_signing/signing.py [342-424]

 class Verifier(metaclass=abc.ABCMeta):
     """Generic signature verifier.
 ...
     """
 
     def verify(self, signature: Signature) -> manifest.Manifest:
         """Verifies the signature.
-...
-    @abc.abstractmethod
-    def _verify_signed_content(self, signature: Signature) -> tuple[str, bytes]:
-        """Verifies the signed content and extract payload type and payload.
-...
+
+        Args:
+            signature: The signature to verify.
+
+        Returns:
+            A `manifest.Manifest` instance that represents the model.
+
+        Raises:
+            ValueError: Signature verification fails.
         """
-    def verify(self, signature: Signature) -> manifest.Manifest:
-        """Verifies the signature.
-...
-    """
         payload_type, payload = self._verify_signed_content(signature)
 
         if payload_type != _IN_TOTO_JSON_PAYLOAD_TYPE:
             raise ValueError(
                 f"Expected DSSE payload {_IN_TOTO_JSON_PAYLOAD_TYPE}, "
                 f"but got {payload_type}"
             )
 
         payload = json.loads(payload)
 
         if payload["_type"] != _IN_TOTO_STATEMENT_TYPE:
             raise ValueError(
                 f"Expected in-toto {_IN_TOTO_STATEMENT_TYPE} payload, "
                 f"but got {payload['_type']}"
             )
 
         return dsse_payload_to_manifest(payload)
 
+    @abc.abstractmethod
+    def _verify_signed_content(self, signature: Signature) -> tuple[str, bytes]:
+        """Verifies the signed content and extract payload type and payload.
+...
+        """
+

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 10

__

Why: The suggestion correctly identifies a duplicated method definition for verify, which is a SyntaxError and will break the application. This is a critical issue that must be fixed.

High
General
Use Payload for digest

Refactor the _digest command to use the Payload class for calculating the root
digest, ensuring consistency with the signing logic and reducing code
duplication.

src/model_signing/_cli.py [400-403]

-hasher = memory.SHA256()
-for descriptor in manifest.resource_descriptors():
-    hasher.update(descriptor.digest.digest_value)
-root_digest = hasher.compute()
+from model_signing._signing.signing import Payload
+payload = Payload(manifest)
+root_digest = payload.statement.subject[0].digest
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly proposes to reuse the Payload class to compute the root digest, which improves code maintainability by reducing duplication and ensuring consistency with the signing logic.

Medium
High-level
Refactor duplicated code in serialization

The serialize methods in file.py and file_shard.py have duplicated file
discovery logic. This should be extracted to a shared utility function to
improve maintainability.

Examples:

src/model_signing/_serialization/file.py [211-222]
src/model_signing/_serialization/file_shard.py [264-275]

Solution Walkthrough:

Before:

# In src/model_signing/_serialization/file.py
class Serializer:
    def serialize(self, model_path, ..., files_to_hash=None):
        paths = []
        if files_to_hash is None:
            files_to_hash = model_path.glob("**/*")
        for path in files_to_hash:
            if should_ignore(path, ...):
                continue
            check_file_or_directory(path, ...)
            if path.is_file():
                paths.append(path)
        # ... hash paths

# In src/model_signing/_serialization/file_shard.py
class Serializer:
    def serialize(self, model_path, ..., files_to_hash=None):
        shards = []
        if files_to_hash is None:
            files_to_hash = model_path.glob("**/*")
        for path in files_to_hash:
            if should_ignore(path, ...):
                continue
            check_file_or_directory(path, ...)
            if path.is_file():
                shards.extend(self._get_shards(path))
        # ... hash shards

After:

# In a new utility file, e.g., src/model_signing/_serialization/util.py
def discover_files_to_process(model_path, ignore_paths, allow_symlinks, files_to_hash=None):
    if files_to_hash is None:
        files_to_hash = model_path.glob("**/*")
    
    for path in files_to_hash:
        if should_ignore(path, ignore_paths):
            continue
        check_file_or_directory(path, allow_symlinks)
        if path.is_file():
            yield path

# In src/model_signing/_serialization/file.py
class Serializer:
    def serialize(self, model_path, ...):
        paths = list(discover_files_to_process(model_path, ...))
        # ... hash paths

# In src/model_signing/_serialization/file_shard.py
class Serializer:
    def serialize(self, model_path, ...):
        shards = []
        for path in discover_files_to_process(model_path, ...):
            shards.extend(self._get_shards(path))
        # ... hash shards
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies significant code duplication in the file discovery logic within the serialize methods of file.py and file_shard.py, and proposing a refactoring to a shared utility is a valid and impactful improvement for code maintainability.

Low
  • More

@SequeI SequeI merged commit a182e42 into main Jan 23, 2026
52 checks passed
@SequeI SequeI deleted the mergeMain branch January 26, 2026 09:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants