Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(protocol): enhance mds errors #341

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

james-d-elliott
Copy link
Member

This adds significant improvements to the MDS3 errors helping identify the root cause of a validation error.

@james-d-elliott james-d-elliott requested a review from a team as a code owner November 27, 2024 08:55
Copy link

coderabbitai bot commented Nov 27, 2024

Walkthrough

The changes primarily focus on enhancing the error handling and validation processes within the VerifyAttestation method of the AttestationObject struct in protocol/attestation.go, as well as the ValidateMetadata function in protocol/metadata.go. A new error variable, ErrMetadata, is introduced in protocol/errors.go to handle specific metadata validation errors. The validateLogin method in webauthn/login.go is updated to include additional parameters for improved credential validation. Overall, these modifications streamline control flows and improve error reporting.

Changes

File Change Summary
protocol/attestation.go Modified VerifyAttestation method to include new error handling logic and streamlined flow.
protocol/errors.go Introduced new error variable ErrMetadata for handling invalid metadata errors.
protocol/metadata.go Updated ValidateMetadata function to return a pointer to Error and added validation for attestationType.
webauthn/login.go Enhanced validateLogin method to include credential.AttestationType for better validation.

Possibly related PRs

  • feat(metadata)!: rework as a provider #239: The changes in protocol/attestation.go regarding the VerifyAttestation method and the introduction of metadata.Provider are related to the modifications in the same file in this PR, which also enhances metadata handling.
  • fix(protocol): ensure attca is parsed correctly #280: The update to the VerifyAttestation method in the main PR aligns with the changes in this PR that introduce a new conditional check within the same method, enhancing the attestation verification process.
  • fix(protocol): ensure attca is parsed correctly #280: The modifications in protocol/attestation_tpm.go regarding the parsing of AIK certificates are relevant as they improve the overall attestation verification process, which is a focus of the main PR's changes to VerifyAttestation.
  • feat(webauthn): include new credential flags func #337: The introduction of the NewCredentialFlags function in this PR complements the changes in the main PR by enhancing the handling of credential flags, which may be relevant in the context of attestation verification and metadata handling.

Poem

🐰 In the meadow where data flows,
New checks and balances, how it glows!
With errors caught and types defined,
Our code now dances, beautifully aligned.
So hop along, let’s validate,
For every byte, we celebrate! 🎉

Warning

Rate limit exceeded

@james-d-elliott has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 9 minutes and 47 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 493a377 and 9ac95ef.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (7)
protocol/errors.go (2)

43-46: Add a default details message for consistency

Unlike other error definitions in this file, ErrMetadata has an empty Details field. Consider adding a default message to maintain consistency and provide immediate context when the error is used without calling WithDetails().

 ErrMetadata = &Error{
   Type:    "invalid_metadata",
-  Details: "",
+  Details: "Error validating metadata statement",
 }

43-46: Consider maintaining alphabetical ordering of error definitions

The new ErrMetadata is placed between ErrInvalidAttestation and ErrAttestationFormat, breaking the apparent alphabetical ordering of error variables. Consider moving it after ErrInvalidAttestation but before ErrNotImplemented to maintain consistent ordering.

protocol/metadata.go (3)

22-22: Typographical Error in Error Message

The word "retrieving" is misspelled as "retreiving" in the error message. Please correct the spelling to improve clarity.

Apply this diff to fix the typo:

- return nil, ErrMetadata.WithInfo(fmt.Sprintf("Failed to validate authenticator metadata for Authenticator Attestation GUID '%s'. Error occurred retreiving the metadata entry: %+v", aaguid, err))
+ return nil, ErrMetadata.WithInfo(fmt.Sprintf("Failed to validate authenticator metadata for Authenticator Attestation GUID '%s'. Error occurred retrieving the metadata entry: %+v", aaguid, err))

49-49: Inconsistent Return Value on Attestation Type Mismatch

When the attestation type is not found, the function returns (entry, ErrMetadata.WithInfo(...)). In other error cases where validation fails, the function returns (nil, error). For consistency and to avoid potential confusion, consider returning (nil, error) when an error occurs.

Apply this diff to make the return value consistent:

- return entry, ErrMetadata.WithInfo(fmt.Sprintf("Failed to validate authenticator metadata for Authenticator Attestation GUID '%s'. The attestation type '%s' is not known to be used by this authenticator.", aaguid.String(), attestationType))
+ return nil, ErrMetadata.WithInfo(fmt.Sprintf("Failed to validate authenticator metadata for Authenticator Attestation GUID '%s'. The attestation type '%s' is not known to be used by this authenticator.", aaguid.String(), attestationType))

55-55: Inconsistent Return Value on Status Validation Error

Similarly, when mds.ValidateStatusReports returns an error, the function returns (entry, error). To maintain consistency across the function, consider returning (nil, error) unless there is a specific reason to return the entry alongside the error.

Apply this diff to align the return values:

- return entry, ErrMetadata.WithInfo(fmt.Sprintf("Failed to validate authenticator metadata for Authenticator Attestation GUID '%s'. Error occurred validating the authenticator status: %+v", aaguid, err))
+ return nil, ErrMetadata.WithInfo(fmt.Sprintf("Failed to validate authenticator metadata for Authenticator Attestation GUID '%s'. Error occurred validating the authenticator status: %+v", aaguid, err))
protocol/attestation.go (1)

190-190: Use the declared context variable ctx

The context variable ctx is declared but not used. Consider using ctx in the call to ValidateMetadata for consistency.

Apply this diff to utilize the ctx variable:

 var protoErr *Error

 ctx := context.Background()

- if entry, protoErr = ValidateMetadata(context.Background(), aaguid, attestationType, mds); protoErr != nil {
+ if entry, protoErr = ValidateMetadata(ctx, aaguid, attestationType, mds); protoErr != nil {
webauthn/login.go (1)

320-323: Consider passing a context instead of using context.Background()

Using context.Background() may limit the ability to handle cancellations and timeouts effectively. Consider passing a context.Context from the caller to allow for better control over the execution flow and resource management.

Apply the following changes:

  1. Modify the call to ValidateMetadata:

    -		if _, protoErr = protocol.ValidateMetadata(context.Background(), aaguid, credential.AttestationType, webauthn.Config.MDS); protoErr != nil {
    +		if _, protoErr = protocol.ValidateMetadata(ctx, aaguid, credential.AttestationType, webauthn.Config.MDS); protoErr != nil {
  2. Update the function signature to accept a context.Context:

    -	func (webauthn *WebAuthn) validateLogin(user User, session SessionData, parsedResponse *protocol.ParsedCredentialAssertionData) (*Credential, error) {
    +	func (webauthn *WebAuthn) validateLogin(ctx context.Context, user User, session SessionData, parsedResponse *protocol.ParsedCredentialAssertionData) (*Credential, error) {
  3. Ensure that all callers of validateLogin pass the appropriate context. For example:

    -	return webauthn.validateLogin(user, session, parsedResponse)
    +	return webauthn.validateLogin(ctx, user, session, parsedResponse)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between e5657ab and 0ee4cb8.

📒 Files selected for processing (4)
  • protocol/attestation.go (1 hunks)
  • protocol/errors.go (1 hunks)
  • protocol/metadata.go (1 hunks)
  • webauthn/login.go (1 hunks)
🔇 Additional comments (2)
protocol/errors.go (1)

43-46: Verify error handling consistency across metadata validation

Let's ensure this new error is properly integrated with appropriate error details where used.

✅ Verification successful

Error handling is consistent but uses WithInfo instead of WithDetails

The error is properly integrated across the codebase in protocol/metadata.go with detailed error messages, but it consistently uses WithInfo() method instead of WithDetails(). All usages provide specific error context including the AAGUID and detailed failure reasons. The error is used in appropriate scenarios:

  • When metadata entry retrieval fails
  • When authenticator has no registered metadata
  • When attestation type validation fails
  • When status report validation fails
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check how ErrMetadata is used across the codebase
# Expect: Usage in ValidateMetadata with WithDetails() calls

# Check for direct ErrMetadata usage
rg "ErrMetadata" -B 2 -A 2

# Check for "invalid_metadata" type usage
rg "invalid_metadata" -B 2 -A 2

# Look for potential error wrapping patterns
ast-grep --pattern 'WithDetails($$$)'

Length of output: 1982

protocol/metadata.go (1)

14-14: Verify the Handling of Nil Metadata Provider

When mds == nil, the function returns (nil, nil). Ensure that this behavior is intentional and that the calling code correctly handles a nil entry and a nil error. If a nil mds should be considered an error, you might want to return an appropriate error to avoid potential nil pointer dereferences.

Run the following script to check how ValidateMetadata is called in the codebase and whether mds can be nil:

✅ Verification successful

Returning nil, nil for a nil Provider is Correct

The current behavior is intentional and safe. From the code analysis:

  1. The webauthn.Config.MDS is explicitly checked for nil before calling ValidateMetadata
  2. The ValidateMetadata function gracefully handles both nil provider and nil entry cases
  3. The metadata validation is an optional feature, as evidenced by the conditional checks in login.go

The function correctly follows the pattern where a nil provider indicates that metadata validation is disabled, which is a valid configuration state. No changes are needed.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Find all calls to ValidateMetadata and check for nil `mds`.

# Search for all usages of ValidateMetadata
rg 'ValidateMetadata\('

# Check if `mds` is ever passed as `nil`
rg -A2 -B2 'ValidateMetadata\([^,]+,[^,]+,[^,]+,\s*nil\)'

Length of output: 552


Script:

#!/bin/bash
# Let's check how webauthn.Config.MDS is used and initialized
rg -A 5 -B 5 'Config.*MDS'

# Let's also check the metadata.Provider interface definition
ast-grep --pattern 'type Provider interface {
  $$$
}'

# And check if there are any tests that verify nil provider behavior
rg -A 5 -B 5 'TestValidateMetadata'

Length of output: 4107


Script:

#!/bin/bash
# Let's check the implementation of ValidateMetadata to understand the nil handling logic
rg -A 20 "func ValidateMetadata"

# Let's also check if there are any tests in protocol package
fd ".*_test.go" protocol/ --exec rg -l "TestValidate"

Length of output: 1340

Comment on lines +41 to +44
if string(atype) == attestationType {
found = true
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Possible Type Conversion Issue in Attestation Type Comparison

In the comparison string(atype) == attestationType, there may be a type mismatch. If atype is not inherently a string, converting it using string(atype) might not yield the expected result. Ensure that atype is properly converted to a string that represents the attestation type.

Apply this diff if atype is of a custom type that implements String():

- if string(atype) == attestationType {
+ if atype.String() == attestationType {

Alternatively, if attestationType can be converted to the same type as atype, consider:

- if string(atype) == attestationType {
+ if atype == metadata.AttestationType(attestationType) {

Please verify that the comparison accurately reflects the intended logic.

Committable suggestion skipped: line range outside the PR's diff.

protocol/attestation.go Outdated Show resolved Hide resolved
This adds significant improvements to the MDS3 errors helping identify the root cause of a validation error.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (2)
protocol/metadata.go (2)

90-90: Fix spelling error in error message

The word "agaisnt" is misspelled and should be "against".

- return ErrMetadata.WithDetails(fmt.Sprintf("Failed to validate attestation statement signature during attestation validation for Authenticator Attestation GUID '%s'. The attestation certificate could not be verified due to an error validating the trust chain agaisnt the Metadata Service.", aaguid))
+ return ErrMetadata.WithDetails(fmt.Sprintf("Failed to validate attestation statement signature during attestation validation for Authenticator Attestation GUID '%s'. The attestation certificate could not be verified due to an error validating the trust chain against the Metadata Service.", aaguid))
🧰 Tools
🪛 golangci-lint (1.62.2)

90-90: agaisnt is a misspelling of against

(misspell)


72-91: Consider using error constants for common error messages

The error messages for certificate validation are quite long and repeated. Consider extracting them into constants for better maintainability and consistency.

Example:

const (
    errNoCertificates       = "The attestation had no certificates"
    errCertificateTypeMismatch = "The first certificate in the attestation was type '%T' but '[]byte' was expected"
    errFullFormatUnsupported = "Attestation was provided in the full format but the authenticator doesn't support the full attestation format"
    // ... more constants
)
🧰 Tools
🪛 golangci-lint (1.62.2)

90-90: agaisnt is a misspelling of against

(misspell)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 0ee4cb8 and 493a377.

📒 Files selected for processing (4)
  • protocol/attestation.go (1 hunks)
  • protocol/errors.go (1 hunks)
  • protocol/metadata.go (2 hunks)
  • webauthn/login.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • protocol/attestation.go
  • protocol/errors.go
  • webauthn/login.go
🧰 Additional context used
🪛 golangci-lint (1.62.2)
protocol/metadata.go

13-13: cyclomatic complexity 21 of func ValidateMetadata is high (> 15)

(gocyclo)


90-90: agaisnt is a misspelling of against

(misspell)

🔇 Additional comments (3)
protocol/metadata.go (3)

Line range hint 23-38: LGTM! Enhanced error messages provide better context

The entry validation logic has been improved with detailed error messages that include the AAGUID, making it easier to diagnose issues.

🧰 Tools
🪛 golangci-lint (1.62.2)

90-90: agaisnt is a misspelling of against

(misspell)


42-48: Ensure safe type conversion in attestation type comparison

The string(atype) conversion might not yield the expected result if atype is not inherently a string.

Consider using the type's String() method or proper type conversion:

- if string(atype) == attestationType {
+ if atype.String() == attestationType {

54-59: LGTM! Clear error handling for status validation

The status validation error handling has been improved with detailed context.

protocol/metadata.go Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (2)
protocol/metadata.go (2)

96-96: Fix spelling error in error message

There's a spelling error in the error message: "agaisnt" should be "against".

Apply this diff:

-				return ErrMetadata.WithDetails(fmt.Sprintf("Failed to validate attestation statement signature during attestation validation for Authenticator Attestation GUID '%s'. The attestation certificate could not be verified due to an error validating the trust chain agaisnt the Metadata Service.", aaguid))
+				return ErrMetadata.WithDetails(fmt.Sprintf("Failed to validate attestation statement signature during attestation validation for Authenticator Attestation GUID '%s'. The attestation certificate could not be verified due to an error validating the trust chain against the Metadata Service.", aaguid))
🧰 Tools
🪛 golangci-lint (1.62.2)

96-96: agaisnt is a misspelling of against

(misspell)


76-82: Consider additional error handling for certificate parsing

While the current error handling is good, consider adding validation for the certificate data length before parsing:

 if data, ok = x5cs[0].([]byte); !ok {
     return ErrMetadata.WithDetails(fmt.Sprintf("Failed to parse attestation certificate from x5c during attestation validation for Authenticator Attestation GUID '%s'.", aaguid)).WithInfo(fmt.Sprintf("The first certificate in the attestation was type '%T' but '[]byte' was expected", x5cs[0]))
 }

+if len(data) == 0 {
+    return ErrMetadata.WithDetails(fmt.Sprintf("Failed to parse attestation certificate from x5c during attestation validation for Authenticator Attestation GUID '%s'.", aaguid)).WithInfo("The certificate data is empty")
+}
+
 if x5c, err = x509.ParseCertificate(data); err != nil {
     return ErrMetadata.WithDetails(fmt.Sprintf("Failed to parse attestation certificate from x5c during attestation validation for Authenticator Attestation GUID '%s'.", aaguid)).WithInfo(fmt.Sprintf("Error returned from x509.ParseCertificate: %+v", err))
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 493a377 and 9ac95ef.

📒 Files selected for processing (4)
  • protocol/attestation.go (1 hunks)
  • protocol/errors.go (1 hunks)
  • protocol/metadata.go (2 hunks)
  • webauthn/login.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • protocol/attestation.go
  • protocol/errors.go
  • webauthn/login.go
🧰 Additional context used
🪛 golangci-lint (1.62.2)
protocol/metadata.go

13-13: cyclomatic complexity 23 of func ValidateMetadata is high (> 15)

(gocyclo)


96-96: agaisnt is a misspelling of against

(misspell)

🔇 Additional comments (4)
protocol/metadata.go (4)

Line range hint 23-38: LGTM! Error handling improvements

The error handling has been enhanced with more descriptive messages using ErrMetadata.WithInfo. The special case handling for zero AAGUID is well-implemented.

🧰 Tools
🪛 golangci-lint (1.62.2)

96-96: agaisnt is a misspelling of against

(misspell)


42-48: Possible Type Conversion Issue in Attestation Type Comparison

The comparison string(atype) == attestationType might not yield the expected result if atype is not inherently a string type.

Let's verify the type definition:

#!/bin/bash
# Search for AttestationType definition
ast-grep --pattern 'type AttestationType'

54-59: LGTM! Clear status validation

The status validation logic is well-structured with proper error handling and descriptive messages.


13-21: 🛠️ Refactor suggestion

Consider decomposing the function to reduce complexity

The function's cyclomatic complexity has increased to 23 (> 15), making it harder to maintain and test. Consider breaking it down into smaller, focused functions.

Consider splitting into these functions:

func validateEntry(ctx context.Context, mds metadata.Provider, aaguid uuid.UUID) *Error
func validateAttestationType(ctx context.Context, entry *metadata.Entry, attestationType string) *Error
func validateStatus(ctx context.Context, mds metadata.Provider, entry *metadata.Entry) *Error
func validateTrustAnchor(ctx context.Context, mds metadata.Provider, entry *metadata.Entry, attestationType string, x5cs []any) *Error
🧰 Tools
🪛 golangci-lint (1.62.2)

13-13: cyclomatic complexity 23 of func ValidateMetadata is high (> 15)

(gocyclo)

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.

1 participant