Skip to content

feat: add OCI spec-compliant descriptor and digest validation#383

Open
sajayantony wants to merge 1 commit into
oras-project:mainfrom
sajayantony:agent/digest-validation-381
Open

feat: add OCI spec-compliant descriptor and digest validation#383
sajayantony wants to merge 1 commit into
oras-project:mainfrom
sajayantony:agent/digest-validation-381

Conversation

@sajayantony

@sajayantony sajayantony commented May 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Add explicit TryValidate/Validate methods to Descriptor and strengthen Digest validation per OCI image-spec v1.1.1 requirements.

Resolves: #381

What Changed

Product Code

src/OrasProject.Oras/Content/Digest.cs

  • Add algorithm constants (AlgorithmSha256, AlgorithmSha512) — no magic strings
  • Enforce per-algorithm encoded format for registered algorithms:
    • sha256: encoded portion MUST match /[a-f0-9]{64}/ (lowercase only, exactly 64 chars)
    • sha512: encoded portion MUST match /[a-f0-9]{128}/ (lowercase only, exactly 128 chars)
  • Allow unrecognized algorithms to pass validation if they match the general grammar (per OCI spec SHOULD)
  • Error constants with .NET framework style messages (sentence case, periods, single-quoted values)
  • Error messages include the invalid digest value for debuggability
  • Use ToLowerInvariant() instead of ToLower() for culture-safe hex encoding

src/OrasProject.Oras/Oci/Descriptor.cs

  • Add Descriptor.TryValidate(out string error) — non-throwing, performance-sensitive validation
  • Add Descriptor.Validate() — convenience wrapper that throws InvalidDescriptorException
  • Validates mediaType (non-empty), size (non-negative), and digest (delegates to Content.Digest.TryValidate)
  • Validate() docs recommend TryValidate for perf-sensitive code paths
  • No validation on property setDescriptor remains a permissive DTO (no breaking change)
  • Fix spec link v1.1.0 -> v1.1.1

src/OrasProject.Oras/Exceptions/InvalidDescriptorException.cs

  • New exception type for descriptor-level validation failures
  • Descriptor.Validate() throws InvalidDescriptorException (not InvalidDigestException)

Error Message Format

Messages follow .NET framework conventions (see FormatException, UriFormatException):

"Invalid digest format. The digest 'notvalid' does not match the required grammar."
"Invalid sha256 digest. Encoded portion must be exactly 64 lowercase hex characters. Got 'sha256:short'."
"Invalid descriptor. The 'mediaType' property must not be empty."

Test Changes

tests/.../Content/ContentTest.cs

  • Updated sha512 test digest to spec-compliant 128-char lowercase hex
  • Changed unrecognized algorithm tests from expecting exceptions to expecting success (per OCI spec)

tests/.../Serialization/ManifestSerializationTest.Negative.cs

  • Negative tests: wrong hex length, uppercase hex, empty mediaType, negative size, invalid format
  • Positive tests: sha256, sha512, unrecognized algorithms, valid descriptor
  • Tests assert the invalid digest value appears in error output
  • Assertions use error constants for stability

tests/.../Exceptions/ExceptionTest.cs

  • Constructor coverage for InvalidDescriptorException

Design Decisions

Decision Rationale
No setter validation Avoids breaking existing consumers; model is a DTO
TryValidate as primary API Server-side pattern — no exceptions for control flow, perf-sensitive
Validate() throws InvalidDescriptorException Semantically correct for descriptor-level errors (mediaType, size, digest)
Permissive for unrecognized algorithms OCI spec: "Implementations SHOULD allow digests with unrecognized algorithms to pass validation"
Error constants + appended value Programmatic matching via StartsWith + debuggability with quoted value
Algorithm constants No magic strings; easy to add new algorithms

Coverage

  • Digest.cs: 100% line, 100% branch
  • Descriptor.cs: 100% line, 100% branch
  • Overall: line and branch coverage increased from baseline

Test Results

  • 512 tests passing

This PR description was generated with AI assistance.

@codecov

codecov Bot commented May 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 92.19%. Comparing base (2706b8a) to head (29be7ff).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #383      +/-   ##
==========================================
+ Coverage   91.92%   92.19%   +0.27%     
==========================================
  Files          67       68       +1     
  Lines        2886     2922      +36     
  Branches      374      380       +6     
==========================================
+ Hits         2653     2694      +41     
+ Misses        139      134       -5     
  Partials       94       94              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sajayantony sajayantony force-pushed the agent/digest-validation-381 branch 2 times, most recently from 63d2692 to 5f3ccd9 Compare May 22, 2026 03:50
@sajayantony sajayantony changed the title feat: validate digest format on Descriptor property set feat: add OCI spec-compliant descriptor and digest validation May 22, 2026
@sajayantony sajayantony marked this pull request as ready for review May 22, 2026 04:41
Copilot AI review requested due to automatic review settings May 22, 2026 04:41
@sajayantony sajayantony requested a review from Wwwsylvia as a code owner May 22, 2026 04:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds OCI image-spec v1.1.1-aligned validation utilities for digests and descriptors, enabling callers to validate descriptor fields explicitly while keeping DTO construction permissive.

Changes:

  • Strengthen Content.Digest validation to strictly enforce sha256/sha512 encoded requirements while allowing unrecognized algorithms that match the general digest grammar.
  • Add Descriptor.TryValidate(out string error) and Descriptor.Validate() to validate mediaType, size, and digest.
  • Expand/adjust tests to cover new digest/descriptor validation behavior and introduce InvalidDescriptorException.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/OrasProject.Oras/Content/Digest.cs Tightens digest validation and adds algorithm-/error-constants.
src/OrasProject.Oras/Oci/Descriptor.cs Adds explicit descriptor validation APIs and error constants; updates spec link.
src/OrasProject.Oras/Exceptions/InvalidDescriptorException.cs Introduces a dedicated exception for descriptor validation failures.
tests/OrasProject.Oras.Tests/Content/ContentTest.cs Updates digest tests for sha512 compliance and unrecognized-algorithm behavior.
tests/OrasProject.Oras.Tests/Serialization/ManifestSerializationTest.Negative.cs Adds positive/negative coverage for descriptor validation (digest/mediaType/size).
tests/OrasProject.Oras.Tests/Exceptions/ExceptionTest.cs Adds constructor coverage for InvalidDescriptorException.

Comment thread src/OrasProject.Oras/Oci/Descriptor.cs
Comment thread src/OrasProject.Oras/Content/Digest.cs Outdated
Comment thread src/OrasProject.Oras/Content/Digest.cs
Comment thread tests/OrasProject.Oras.Tests/Content/ContentTest.cs
Copilot AI review requested due to automatic review settings May 22, 2026 04:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Comment thread src/OrasProject.Oras/Oci/Descriptor.cs Outdated
Comment thread src/OrasProject.Oras/Oci/Descriptor.cs
Copilot AI review requested due to automatic review settings May 22, 2026 06:04
Add explicit TryValidate/Validate methods to Descriptor and strengthen
Digest validation per OCI image-spec v1.1.1 requirements.

- Add algorithm constants (AlgorithmSha256, AlgorithmSha512)
- Enforce per-algorithm encoded format for registered algorithms
- Allow unrecognized algorithms per OCI spec SHOULD clause
- Add Descriptor.TryValidate(out string error) as primary API
- Add Descriptor.Validate() throwing InvalidDescriptorException
- Validate mediaType (non-empty), size (non-negative), digest format
- Error messages follow .NET framework conventions with quoted values
- 100% line and branch coverage on Digest.cs and Descriptor.cs

Resolves: oras-project#381

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Sajay Antony <sajaya@microsoft.com>
@sajayantony sajayantony force-pushed the agent/digest-validation-381 branch from 2d2c228 to 29be7ff Compare May 22, 2026 06:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.

Comments suppressed due to low confidence (1)

tests/OrasProject.Oras.Tests/Serialization/ManifestSerializationTest.Negative.cs:273

  • The sha512 InlineData literal here makes the line exceed the repo’s 120-column limit. Similar to the other cases in this file, consider using const string fields for long digests and referencing those constants from InlineData.
    [Theory]
    [InlineData("sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a")]
    [InlineData("sha512:cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e")]
    public void Descriptor_TryValidate_ValidDigest_ReturnsTrue(string validDigest)

Comment thread src/OrasProject.Oras/Registry/Remote/Referrers.cs
Comment thread tests/OrasProject.Oras.Tests/Content/ContentTest.cs
Comment thread src/OrasProject.Oras/Oci/Descriptor.cs
Comment thread src/OrasProject.Oras/Content/Digest.cs
/// <returns>true if the descriptor is valid; otherwise, false.</returns>
public bool TryValidate(out string error)
{
if (string.IsNullOrWhiteSpace(MediaType))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TryValidate's XML doc promises OCI image-spec v1.1.1 descriptor validation, but this currently only checks that mediaType is non-empty. Per the descriptor spec, mediaType MUST comply with RFC 6838, and artifactType has the same requirement when present; repo guidance also calls out optional artifactType non-empty validation. As written, descriptors such as MediaType = "not a media type" or ArtifactType = "" can pass the new validation API.

Can we either reuse/extract the existing media-type validator from Packer and apply it to MediaType plus non-null/non-empty ArtifactType, or narrow the docs to say this is only minimal structural validation rather than full OCI descriptor validation?

internal static bool IsNullOrInvalid(Descriptor? descriptor)
{
return descriptor == null || string.IsNullOrWhiteSpace(descriptor.Digest) || string.IsNullOrWhiteSpace(descriptor.MediaType);
return descriptor == null || string.IsNullOrWhiteSpace(descriptor.Digest) || string.IsNullOrWhiteSpace(descriptor.MediaType) || descriptor.Size < 0;

@akashsinghal akashsinghal May 22, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: The repo guidance sets a hard 120-column limit, and the current PR head still has several added lines over that limit despite the earlier resolved thread saying this was fixed. A consolidated list from the current diff:

  • src/OrasProject.Oras/Content/Digest.cs:30-31 — 138/139 columns for the digest error constants.
  • src/OrasProject.Oras/Oci/Descriptor.cs:83 — 156 columns for this IsNullOrInvalid return expression.
  • tests/OrasProject.Oras.Tests/Content/ContentTest.cs:40 — 155-column sha512 InlineData.
  • tests/OrasProject.Oras.Tests/Exceptions/ExceptionTest.cs:73-74 — 127/133 columns.
  • tests/OrasProject.Oras.Tests/Serialization/ManifestSerializationTest.Negative.cs:157 and :272 — long sha512 InlineData values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants