Skip to content

Conversation

jamlo
Copy link
Contributor

@jamlo jamlo commented Jan 7, 2025

Add License Validation using JWT and JWKS

This PR introduces a robust license validation system using JSON Web Tokens (JWT) and JSON Web Key Sets (JWKS). The implementation provides cryptographic verification of license tokens using public key cryptography, specifically supporting both RSA and ECDSA algorithms.

Overview

The license validation system is built on two modern standards:

  1. JWT (JSON Web Tokens) for the license token format
  2. JWKS (JSON Web Key Sets) for public key distribution

License tokens are cryptographically signed JWTs that contain claims about the license, such as:

  • Features enabled
  • Usage limitations
  • Expiration dates
  • License metadata
  • Customer ID
  • License ID

Example License Data:

{
      "product": "Bacalhau",
      "license_id": "license_1",
      "license_type": "prod_tier_1",
      "license_version": "v_1",
      "customer_id": "customer_1",
      "capabilities": {"tier_1": "no"},
      "metadata": {"something1": "something1_value"}
}

The validation system uses JWKS to maintain a set of trusted public keys that can verify these license tokens. Each key in the JWKS has a unique key ID (kid), allowing for key rotation and multiple signing authorities.

Implementation Details

  • Uses github.com/golang-jwt/jwt/v5 for JWT handling
  • Uses github.com/MicahParks/keyfunc/v3 for JWKS processing
  • Support for both RSA and ECDSA signing algorithms
  • Custom claims validation for license-specific requirements

Example usage:

// Initialize validator with embedded JWKS
validator, err := license.NewLicenseValidatorFromJSON(embeddedJWKS)
if err != nil {
    log.Fatalf("Failed to create validator: %v", err)
}

// Validate a license token
claims, err := validator.ValidateToken(licenseToken)
if err != nil {
    log.Fatalf("License validation failed: %v", err)
}

// Access license features and limitations
fmt.Printf("Licensed capabilities: %v\n", claims.Capabilities)

Future Extensibility

While the current implementation embeds the JWKS in the binary, the system is designed to be easily extended to fetch JWKS from a well-known URL endpoint. This follows the standard practice used in OAuth2 and OpenID Connect where JWKs are typically available at /.well-known/jwks.json.

This future enhancement would enable:

  • Dynamic key rotation
  • Real-time key revocation
  • Multiple signing authorities
  • Zero-downtime key updates

Security Considerations

  • Uses industry-standard cryptographic algorithms (RSA/ECDSA)
  • No secret keys stored in the binary (only public keys)
  • Support for key rotation via key IDs
  • Validation of all JWT claims including expiration and issuance time
  • Protection against common JWT attacks through proper validation

Summary by CodeRabbit

Release Notes

  • New Features

    • Enhanced license validation with support for JSON Web Token (JWT) and JSON Web Key Sets (JWKS)
    • Added robust token verification mechanisms with detailed claim validation
  • Dependencies

    • Updated JWT library to latest version
    • Added new dependencies for improved token and key management
  • Documentation

    • Added comprehensive README instructions for license validation and token generation
    • Included code examples for initializing license validators and handling token verification
  • Tests

    • Introduced extensive unit tests for license validation functionality
    • Covered various scenarios including token expiration, claim validation, and error handling

This commit introduces a robust license validation system using JSON Web
Tokens (JWT) and JSON Web Key Sets (JWKS). The implementation provides
cryptographic verification of license tokens using public key
cryptography, specifically supporting both RSA and ECDSA algorithms.

The license validation system is built on two modern standards:
1. JWT (JSON Web Tokens) for the license token format
2. JWKS (JSON Web Key Sets) for public key distribution

License tokens are cryptographically signed JWTs that contain claims
about the license, such as:
- Features enabled
- Usage limitations
- Expiration dates
- License metadata

Linear: https://linear.app/expanso/issue/ENG-512/license-inspection-and-tooling
Copy link

linear bot commented Jan 7, 2025

Copy link
Contributor

coderabbitai bot commented Jan 7, 2025

Walkthrough

This pull request introduces a comprehensive license validation system for JWT tokens using JSON Web Key Sets (JWKS). The changes include updating Go module dependencies, implementing a new LicenseValidator struct in license_validator.go, adding extensive unit tests in license_validator_test.go, and updating the README with usage instructions. The implementation focuses on robust token validation, including custom claims, additional constraint checks, and support for file-based and JSON-based JWKS initialization.

Changes

File Change Summary
go.mod - Added github.com/MicahParks/keyfunc/v3 v3.3.5 dependency
- Updated github.com/golang-jwt/jwt from v3.2.2 to v5.2.0
pkg/lib/license/license_validator.go - Added LicenseValidator struct
- Added LicenseClaims struct
- Implemented token validation methods:
- NewLicenseValidatorFromFile
- NewLicenseValidatorFromJSON
- ValidateToken
- validateAdditionalConstraints
pkg/lib/license/license_validator_test.go - Added comprehensive unit tests for:
- License validator initialization
- Token validation
- Claims extraction
- Error handling scenarios
pkg/lib/license/README.md - Added sections on license validation
- Included code examples for JWKS and JWT token generation

Sequence Diagram

sequenceDiagram
    participant Client
    participant LicenseValidator
    participant JWKS
    participant JWT

    Client->>LicenseValidator: Initialize with JWKS
    LicenseValidator->>JWKS: Load Key Set
    Client->>LicenseValidator: Validate Token
    LicenseValidator->>JWT: Parse and Verify
    LicenseValidator->>LicenseValidator: Check Additional Constraints
    LicenseValidator-->>Client: Return Validated Claims
Loading

Poem

🐰 A Rabbit's License Validation Tale 🔑

With JWKS and tokens so bright,
Validating licenses, our code takes flight!
Claims checked with precision and care,
No unauthorized access shall dare.
Security hops, our validator's might! 🚀


🪧 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 generate docstrings to generate docstrings for this PR. (Beta)
  • @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.

@jamlo jamlo requested a review from wdbaruni January 7, 2025 05:41
@jamlo jamlo marked this pull request as ready for review January 7, 2025 05:41
Copy link
Contributor

@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 comments (2)
go.mod (2)

Line range hint 3-3: Fix invalid Go version.

go 1.23 specified in the go.mod file is invalid as Go 1.23 hasn't been released yet. The latest stable version is Go 1.22.

-go 1.23
+go 1.22

Line range hint 18-18: Remove old JWT library to prevent conflicts.

Both v3 and v5 of the JWT library are present which could lead to dependency conflicts and confusion. Since you're implementing new JWT functionality, standardize on v5.

-	github.com/golang-jwt/jwt v3.2.2+incompatible

Also applies to: 25-25

🧹 Nitpick comments (7)
pkg/lib/license/license_validator.go (5)

43-62: Simplify JWKS validation in NewLicenseValidatorFromJSON

The manual parsing and validation of the JWKS JSON may be redundant since keyfunc.NewJWKSetJSON already handles parsing and structure validation of the JWKS. Consider removing the explicit JSON unmarshalling and checks to simplify the code.

Apply this diff to streamline the function:

 func NewLicenseValidatorFromJSON(jwksJSON json.RawMessage) (*LicenseValidator, error) {
-	if len(jwksJSON) == 0 {
-		return nil, fmt.Errorf("empty JWKS JSON")
-	}
-
-	// Parse the JSON first to validate structure
-	var jwks struct {
-		Keys []interface{} `json:"keys"`
-	}
-	if err := json.Unmarshal(jwksJSON, &jwks); err != nil {
-		return nil, fmt.Errorf("invalid JWKS JSON: %w", err)
-	}
-
-	// Check if keys array exists and is not empty
-	if jwks.Keys == nil {
-		return nil, fmt.Errorf("missing 'keys' array in JWKS")
-	}
-	if len(jwks.Keys) == 0 {
-		return nil, fmt.Errorf("empty 'keys' array in JWKS")
-	}

 	// Create the JWKS key function
 	keyFunc, err := keyfunc.NewJWKSetJSON(jwksJSON)
 	if err != nil {
 		return nil, fmt.Errorf("failed to create JWKS key function: %w", err)
 	}

 	return &LicenseValidator{
 		keyFunc: keyFunc.Keyfunc,
 	}, nil
 }

81-83: Enhance error handling for token parsing

Currently, when token parsing fails, the error message is wrapped with "failed to parse token" using %w. To provide clearer error messages without exposing sensitive information, consider using %v or adding more context to the error.

Apply this diff to adjust the error reporting:

 	if err != nil {
-		return nil, fmt.Errorf("failed to parse token: %w", err)
+		return nil, fmt.Errorf("failed to parse token: %v", err)
 	}

85-87: Remove redundant token validity check

The jwt.ParseWithClaims function already validates the token and returns an error if the token is invalid. Checking if !token.Valid immediately after parsing is redundant. Consider removing this check to simplify the code.

Apply this diff to eliminate the redundant code:

 	if err != nil {
 		return nil, fmt.Errorf("failed to parse token: %v", err)
 	}
-
-	if !token.Valid {
-		return nil, fmt.Errorf("invalid token")
-	}

97-113: Utilize built-in claims validation

The jwt.RegisteredClaims type provides a Valid() method that performs standard validation of registered claims like ExpiresAt and NotBefore. Replace the manual checks with this method to ensure consistent and thorough validation.

Apply this diff to use the built-in validation:

 func (v *LicenseValidator) validateAdditionalConstraints(claims *LicenseClaims) error {
-	now := time.Now()
-
-	// Check if token is expired
-	if claims.ExpiresAt != nil && claims.ExpiresAt.Before(now) {
-		return fmt.Errorf("license has expired")
-	}
-
-	// Check if token is not yet valid
-	if claims.NotBefore != nil && claims.NotBefore.After(now) {
-		return fmt.Errorf("license is not yet valid")
-	}
+	if err := claims.RegisteredClaims.Valid(); err != nil {
+		return fmt.Errorf("token validation error: %v", err)
+	}

 	// Add any other custom validation logic here
 	return nil
 }

9-10: Consider implementing JWKS refresh for key rotation

If the public keys in the JWKS might change over time due to key rotation, consider implementing a mechanism to refresh the JWKS periodically. The keyfunc package supports fetching JWKS from a URL with auto-refresh capabilities.

This enhancement improves security by ensuring that the validator uses the most recent set of keys.

pkg/lib/license/license_validator_test.go (1)

96-157: Expand test coverage for token validation

Currently, the TestValidateToken function does not include tests for expired or not-yet-valid tokens. Adding these test cases will ensure that the validateAdditionalConstraints method correctly handles tokens outside their valid time window.

Would you like assistance in adding these test cases?

🧰 Tools
🪛 Gitleaks (8.21.2)

130-130: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


136-136: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

cspell.yaml (1)

36-36: Re-evaluate the need to ignore the test file in spell checking

Adding pkg/lib/license/license_validator_test.go to ignorePaths excludes it from spell checking, which might allow typos to slip through. Consider removing this entry and instead add any necessary words to the custom dictionary or address specific spelling errors within the test file.

This approach maintains code quality by ensuring all files are checked for spelling errors.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6384d4a and eb3453b.

⛔ Files ignored due to path filters (2)
  • go.sum is excluded by !**/*.sum
  • go.work.sum is excluded by !**/*.sum
📒 Files selected for processing (4)
  • cspell.yaml (1 hunks)
  • go.mod (3 hunks)
  • pkg/lib/license/license_validator.go (1 hunks)
  • pkg/lib/license/license_validator_test.go (1 hunks)
🧰 Additional context used
🪛 Gitleaks (8.21.2)
pkg/lib/license/license_validator_test.go

34-34: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


35-35: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


130-130: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


136-136: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

🔇 Additional comments (3)
pkg/lib/license/license_validator.go (1)

22-30: Ensure required claims are properly validated

All custom claims in LicenseClaims are tagged with omitempty, which means they are optional during JSON unmarshalling. If certain claims are mandatory for license validation, remove the omitempty tag to enforce their presence.

Please verify which claims are required and adjust the struct tags accordingly.

pkg/lib/license/license_validator_test.go (1)

211-212: Verify the accuracy of the expiration timestamp in test claims

The test asserts that claims.ExpiresAt.Unix() equals 10376240107, which corresponds to a date approximately in the year 3300. Confirm that this timestamp is intentional and that it aligns with the expected behavior of the license expiration.

Adjust the timestamp if a more realistic expiration time is intended.

Also applies to: 225-226

go.mod (1)

8-8: Good choice of JWT/JWKS libraries.

The selected libraries for JWT (github.com/golang-jwt/jwt/v5) and JWKS (github.com/MicahParks/keyfunc/v3 and its dependency github.com/MicahParks/jwkset) are well-maintained, popular choices that provide the necessary functionality for secure license validation.

Let's verify the latest versions and security advisories for these dependencies:

Also applies to: 25-25, 92-92

✅ Verification successful

Verified: JWT/JWKS libraries are secure and well-maintained

The project uses the latest versions (jwt v5.x.x, keyfunc v3.3.5) of both libraries, which are actively maintained with recent updates (Nov 2024 and Sept 2024 respectively) and have no known security vulnerabilities.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for security advisories in the JWT and JWKS libraries
gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: GO, package: "github.com/golang-jwt/jwt") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}
'

gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: GO, package: "github.com/MicahParks/keyfunc") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}
'

Length of output: 720


Script:

#!/bin/bash
# Check latest versions and activity for JWT libraries
curl -s "https://pkg.go.dev/github.com/golang-jwt/jwt/v5?tab=versions" | grep -o 'v[0-9]\+\.[0-9]\+\.[0-9]\+'
curl -s "https://pkg.go.dev/github.com/MicahParks/keyfunc/v3?tab=versions" | grep -o 'v[0-9]\+\.[0-9]\+\.[0-9]\+'

# Check recent activity
gh api repos/golang-jwt/jwt/commits?per_page=1
gh api repos/MicahParks/keyfunc/commits?per_page=1

# Check popularity
curl -s "https://pkg.go.dev/github.com/golang-jwt/jwt/v5" | grep -o "[0-9]\+[KM]\? imports"
curl -s "https://pkg.go.dev/github.com/MicahParks/keyfunc/v3" | grep -o "[0-9]\+[KM]\? imports"

Length of output: 10794

Copy link
Member

@wdbaruni wdbaruni left a comment

Choose a reason for hiding this comment

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

Nice!

- Add more tests to cover them
- Add README to cover tokens generation for tests
Copy link
Contributor

@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

🧹 Nitpick comments (5)
pkg/lib/license/license_validator_test.go (1)

260-264: Consider using a cross-platform approach to test invalid file permissions

The test case uses the hardcoded file path /root/test.json, which may not behave consistently across different operating systems or environments. On non-Unix systems or when tests are run with elevated permissions, this path might exist or be accessible, causing the test to produce unreliable results.

Consider creating a temporary file and programmatically setting its permissions to an inaccessible state using os.Chmod. This approach ensures the test reliably simulates the scenario of invalid file permissions across all environments.

Example:

import (
	"io/ioutil"
	"os"
)

// In your test function
file, err := ioutil.TempFile("", "test.json")
require.NoError(t, err)
defer os.Remove(file.Name())

// Set file permissions to 000 to simulate invalid permissions
err = os.Chmod(file.Name(), 0000)
require.NoError(t, err)

validator, err := NewLicenseValidatorFromFile(file.Name())
// Continue with your assertions
pkg/lib/license/README.md (3)

1-1: Remove trailing colons from headings

The headings on lines 1 and 21 have trailing colons, which is against Markdown style guidelines. Removing the colons will improve the readability and adhere to standard Markdown practices.

Apply this diff:

-## How to verify a license:
+## How to verify a license

...

-## How to generate JWKS and JWT tokens for tests:
+## How to generate JWKS and JWT tokens for tests

Also applies to: 21-21

🧰 Tools
🪛 Markdownlint (0.37.0)

1-1: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


1-1: null
First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)


1-1: Add a top-level heading at the beginning of the file

Markdown linting suggests that the first line should be a top-level heading (#). Adding a main heading will provide a clear title for the README and improve navigation.

Example:

+# License Validation Guide
+
 ## How to verify a license
🧰 Tools
🪛 Markdownlint (0.37.0)

1-1: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


1-1: null
First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)


32-36: Align the comment with the code

The comment states "Generate four RSA key pairs," but the code only generates one key pair (rsa_key_1). To avoid confusion, either update the comment to reflect the actual code or modify the code to generate four key pairs as indicated.

Option 1: Update the comment:

-    # Generate four RSA key pairs
+    # Generate an RSA key pair

Option 2: Update the code to generate four key pairs (if intended):

# Generate four RSA key pairs
rsa_keys = []
for i in range(4):
    rsa_key = jwk.JWK.generate(kty='RSA', size=2048, alg='RS256', use='sig', kid=f'{key_name}_{i+1}')
    rsa_keys.append(rsa_key)
    # Save keys and tokens as needed
pkg/lib/license/license_validator.go (1)

110-140: Define constants for expected values to enhance maintainability

Hardcoding expected values like the license version "v1" and issuer "https://expanso.io/" can introduce risks of typos and make future updates more cumbersome. Consider defining these values as constants to improve code maintainability and readability.

Example:

const (
	ExpectedLicenseVersion = "v1"
	ExpectedIssuer         = "https://expanso.io/"
)

// Then use:
if claims.LicenseVersion == ExpectedLicenseVersion {
	// ...
	if claims.Issuer != ExpectedIssuer {
		return fmt.Errorf("invalid issuer: expected '%s', got '%s'", ExpectedIssuer, claims.Issuer)
	}
} else {
	return fmt.Errorf("unsupported license version: %s", claims.LicenseVersion)
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb3453b and a919596.

📒 Files selected for processing (3)
  • pkg/lib/license/README.md (1 hunks)
  • pkg/lib/license/license_validator.go (1 hunks)
  • pkg/lib/license/license_validator_test.go (1 hunks)
🧰 Additional context used
📓 Learnings (1)
pkg/lib/license/license_validator_test.go (1)
Learnt from: jamlo
PR: bacalhau-project/bacalhau#4799
File: pkg/lib/license/license_validator_test.go:34-35
Timestamp: 2025-01-07T05:49:03.470Z
Learning: In the context of testing JWT functionality, it is acceptable and preferred to use hardcoded JWT tokens with test data for better clarity and maintainability, even if static analysis tools flag them as potential security issues.
🪛 Markdownlint (0.37.0)
pkg/lib/license/README.md

1-1: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


21-21: Punctuation: ':'
Trailing punctuation in heading

(MD026, no-trailing-punctuation)


1-1: null
First line in a file should be a top-level heading

(MD041, first-line-heading, first-line-h1)

🪛 Gitleaks (8.21.2)
pkg/lib/license/license_validator_test.go

111-111: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


112-112: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


113-113: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


117-117: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


118-118: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


120-120: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


121-121: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


122-122: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


123-123: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


124-124: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


219-219: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


225-225: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: testcontainers-suite / tests
🔇 Additional comments (2)
pkg/lib/license/license_validator.go (2)

18-29: Well-structured LicenseClaims with agreed-upon fields

The LicenseClaims struct is well-defined and incorporates the Capabilities field as a map[string]string, aligning with the design decisions discussed in previous reviews. The removal of Features and Limitations in favor of Capabilities enhances flexibility and adheres to the agreed-upon approach.


96-143: Comprehensive validation in validateAdditionalConstraints

The validateAdditionalConstraints function provides thorough validation of the license claims, including checks for token expiration, not-before constraints, required fields, and issuer verification. The error messages are clear and informative, aiding in debugging and ensuring that invalid tokens are properly rejected.

Copy link
Contributor

@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

🧹 Nitpick comments (4)
pkg/lib/license/license_validator_test.go (4)

112-127: Consider using constants for common test values.

While the JWT tokens are well organized, there are repeated values like "customer_1", "license_1", etc. Consider extracting these as constants for better maintainability.

+const (
+    testCustomerID1 = "customer_1"
+    testLicenseID1  = "license_1"
+    testIssuer      = "https://expanso.io/"
+    testProduct     = "Bacalhau"
+)
🧰 Tools
🪛 Gitleaks (8.21.2)

113-113: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


114-114: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


115-115: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


119-119: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


120-120: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


122-122: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


123-123: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


124-124: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


125-125: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


126-126: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


250-280: Add positive test case for file-based validator.

The test only covers error cases. Consider adding a positive test case with a temporary file containing valid JWKS data.

 func TestNewLicenseValidatorFromFile(t *testing.T) {
+    // Create a temporary file with valid JWKS
+    tmpFile, err := os.CreateTemp("", "jwks_*.json")
+    require.NoError(t, err)
+    defer os.Remove(tmpFile.Name())
+    
+    _, err = tmpFile.Write(testJWKS)
+    require.NoError(t, err)
+    require.NoError(t, tmpFile.Close())
+
     tests := []struct {
         name     string
         filename string
         wantErr  bool
     }{
+        {
+            name:     "Valid JWKS file",
+            filename: tmpFile.Name(),
+            wantErr:  false,
+        },
         {

339-347: Consider adding more time-based test cases.

While the test verifies expired tokens, consider adding cases for:

  • Tokens that are not yet valid (before NBF)
  • Tokens that are about to expire
  • Tokens with invalid time formats

1-424: Consider adding test coverage for edge cases.

While the test coverage is comprehensive, consider adding tests for:

  1. Very large tokens that might exceed size limits
  2. Tokens with unsupported algorithms
  3. Tokens with invalid claim types (e.g., numbers instead of strings)
  4. Concurrent validation of multiple tokens
🧰 Tools
🪛 Gitleaks (8.21.2)

113-113: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


114-114: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


115-115: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


119-119: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


120-120: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


122-122: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


123-123: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


124-124: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


125-125: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


126-126: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


221-221: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


227-227: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a919596 and b9bf01c.

📒 Files selected for processing (1)
  • pkg/lib/license/license_validator_test.go (1 hunks)
🧰 Additional context used
📓 Learnings (1)
pkg/lib/license/license_validator_test.go (1)
Learnt from: jamlo
PR: bacalhau-project/bacalhau#4799
File: pkg/lib/license/license_validator_test.go:34-35
Timestamp: 2025-01-07T05:49:03.470Z
Learning: In the context of testing JWT functionality, it is acceptable and preferred to use hardcoded JWT tokens with test data for better clarity and maintainability, even if static analysis tools flag them as potential security issues.
🪛 Gitleaks (8.21.2)
pkg/lib/license/license_validator_test.go

113-113: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


114-114: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


115-115: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


119-119: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


120-120: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


122-122: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


123-123: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


124-124: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


125-125: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


126-126: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


221-221: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


227-227: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: testcontainers-suite / tests
🔇 Additional comments (7)
pkg/lib/license/license_validator_test.go (7)

11-12: LGTM! Good practice adding spell check disable comment.

The comment clearly explains why spell checking is disabled for this file.


13-40: LGTM! Well-structured JWKS test data.

The test JWKS is comprehensive and includes multiple RSA keys for different test scenarios.


42-109: LGTM! Comprehensive test JWKS for invalid scenarios.

The test JWKS for invalid tokens covers various error cases with descriptive key IDs.


129-185: LGTM! Well-structured table-driven tests for JWKS validation.

The test cases comprehensively cover various scenarios including:

  • Valid JWKS
  • Invalid JSON format
  • Empty JSON
  • Empty and null keys array

187-248: LGTM! Comprehensive token validation test cases.

The test cases cover essential scenarios:

  • Valid RSA tokens
  • Empty tokens
  • Invalid format
  • Unknown key ID
  • Invalid signatures
🧰 Tools
🪛 Gitleaks (8.21.2)

221-221: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


227-227: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)


282-337: LGTM! Thorough validation of token claims.

The test comprehensively verifies all claim fields including:

  • Standard JWT claims (iss, sub, exp, nbf)
  • Custom license claims (product, license_id, license_type, etc.)
  • Capabilities and metadata

349-424: LGTM! Comprehensive validation of token constraints.

The test thoroughly covers validation of:

  • License version
  • Product name
  • Required fields (license_id, license_type, customer_id)
  • Issuer validation
  • Subject and JTI requirements

@jamlo jamlo merged commit 4a6cde1 into main Jan 7, 2025
14 checks passed
@jamlo jamlo deleted the jamlo/eng-512/license-inspection-and-tooling branch January 7, 2025 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

3 participants