Skip to content

Refactor 2 #15

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

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open

Refactor 2 #15

wants to merge 17 commits into from

Conversation

patinthehat
Copy link
Member

@patinthehat patinthehat commented Sep 15, 2023

This PR refactors the integrations functionality:

  • reorganize dotenv vault integration
  • add infisical integration
  • add generic integration interface and run all integrations on workflow init

Summary by CodeRabbit

  • New Features
    • Added integration with Infisical, enabling automatic loading of secrets from Infisical workspaces into the environment.
    • Introduced dotenv-vault integration for loading environment variables from a .env.vault file.
    • Enhanced workflow system to support multiple integrations, configurable via the environment section.
  • Documentation
    • Updated README to include instructions and examples for using the new Infisical integration.
  • Tests
    • Added comprehensive unit tests for Infisical and dotenv-vault integrations, as well as workflow and task functionalities.
  • Chores
    • Added Codecov configuration for improved code coverage reporting.
    • Updated build workflow triggers and improved checksum update tasks.
    • Added a new indirect dependency for testing support.

@what-the-diff
Copy link

what-the-diff bot commented Sep 15, 2023

PR Summary

  • Introduction of Infisical Integration

    • The README.md and workflow.go files were updated to incorporate a new feature that integrates with Infisical workspace. This new feature permits us to load secrets from the Infisical workspace. An example of how we can load secrets has been included in the README.md file for more clarity.
  • Updated Checksum Generation

    • The update-checksums task in the Taskfile.dist.yaml file now generates checksums for YAML files in a specific directory, which improves our file verification process. It also removes the prefix from the checksum file, thereby resulting in cleaner file paths.
  • Addition of dotenv-vault Integration

    • Two new files (dotenv_vault.go and client.go) have been introduced which implement a dotenv-vault integration. This allows for loading secrets from a .env.vault file, serving as a secure way to handle sensitive data.
  • Introduction of Infisical Integration Tests

    • Two additional files (client.go and infiscal_test.go) have been added that implement tests for the Infisical integration. The inclusion of these tests ensures that the new integration behaves as expected and helps prevent potential issues.
  • New List Function for Available Integrations

    • The integration.go file has been updated to include a new List function. This function gives us a compilation of all available integrations for better visibility. Currently, it includes the newly added dotenv-vault and infisical integrations.

@codecov
Copy link

codecov bot commented Sep 15, 2023

Codecov Report

Patch coverage: 40.91% and project coverage change: +2.01% 🎉

Comparison is base (cd0bd64) 26.19% compared to head (f6016ee) 28.21%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main      #15      +/-   ##
==========================================
+ Coverage   26.19%   28.21%   +2.01%     
==========================================
  Files          18       18              
  Lines        2012     2028      +16     
==========================================
+ Hits          527      572      +45     
+ Misses       1444     1415      -29     
  Partials       41       41              
Files Changed Coverage Δ
lib/app/tasks.go 12.27% <25.00%> (+12.27%) ⬆️
lib/app/workflow.go 11.74% <44.44%> (+11.74%) ⬆️

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

@patinthehat
Copy link
Member Author

@coderabbitai summary

Copy link

coderabbitai bot commented Jun 9, 2025

✅ Actions performed

Summary regeneration triggered.

Copy link

coderabbitai bot commented Jun 9, 2025

Walkthrough

This update introduces new integrations for secret management, specifically Infisical and dotenv-vault, along with their client libraries and test coverage. The workflow and task management code is refactored to support integrations, and documentation is updated accordingly. Codecov configuration and build workflow triggers are also adjusted.

Changes

File(s) Change Summary
.github/codecov.yml Added Codecov config for coverage precision, rounding, and status thresholds.
.github/workflows/build-and-test.yml Removed pull_request_target trigger; simplified event triggers.
README.md Documented Infisical integration, usage, and example; updated ToC.
Taskfile.dist.yaml Refactored update-checksums task: explicit paths, path prefix trimming, removed git commands.
go.mod Added indirect dependency on github.com/stretchr/objx v0.5.0.
lib/app/tasks.go Exported CanRunOnCurrentPlatform and CanRunConditionally methods.
lib/app/tasks_test.go Added tests for Task methods using a mock JS engine.
lib/app/workflow.go Added Integrations map, methods for contract/env section, and integration initialization logic.
lib/app/workflow_test.go Added tests for workflow creation, contract, and task lookup.
lib/integrations/dotenv_vault/dotenv_vault.go Added dotenv-vault integration: loads .env.vault if enabled.
lib/integrations/dotenv_vault/dotenv_vault_test.go Added tests and mocks for dotenv-vault integration.
lib/integrations/infisical/client.go Added Infisical API client: supports secret CRUD, decryption, and token management.
lib/integrations/infisical/infiscal_test.go Added tests for Infisical client: covers all major API methods with mock server.
lib/integrations/infisical/infisical.go Added Infisical integration: parses env entries, fetches secrets, injects into environment.
lib/integrations/integration.go Introduced Integration interface and registry for dotenv-vault and Infisical.
lib/types/types.go Added GetEnvSection() method to AppWorkflowContract interface.

Sequence Diagram(s)

Integration Initialization and Run Flow

sequenceDiagram
    participant Workflow
    participant Integrations
    participant Integration
    participant EnvProvider

    Workflow->>Integrations: List(getWorkflow)
    Integrations->>Integration: New(getWorkflow)
    loop For each Integration
        Workflow->>Integration: IsEnabled()
        alt If enabled
            Workflow->>Integration: Run()
            Integration->>EnvProvider: Load secrets (if applicable)
            EnvProvider->>Workflow: Set environment variables
        end
    end
Loading

Infisical Integration Secret Injection

sequenceDiagram
    participant Workflow
    participant InfisicalIntegration
    participant InfisicalClient
    participant InfisicalAPI

    Workflow->>InfisicalIntegration: Run()
    loop For each env entry
        InfisicalIntegration->>InfisicalIntegration: parseInfisicalURL(entry)
        InfisicalIntegration->>InfisicalClient: GetSecrets(workspace, env, path)
        InfisicalClient->>InfisicalAPI: Fetch secrets
        InfisicalAPI-->>InfisicalClient: Encrypted secrets
        InfisicalClient->>InfisicalIntegration: Decrypted secrets
        InfisicalIntegration->>Workflow: Set env variables
    end
Loading

Poem

A rabbit hopped through secrets' grass,
With Infisical now, none shall pass—
Dotenv-vault joins the warren too,
Integrations fresh as morning dew.
Tests abound, the coverage grows,
In every burrow, confidence shows!
🥕✨

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

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.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this 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.

@patinthehat
Copy link
Member Author

@CodeRabbit full review

Copy link

coderabbitai bot commented Jun 9, 2025

✅ Actions performed

Full review triggered.

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: 21

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between cd0bd64 and f6016ee.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (16)
  • .github/codecov.yml (1 hunks)
  • .github/workflows/build-and-test.yml (0 hunks)
  • README.md (2 hunks)
  • Taskfile.dist.yaml (1 hunks)
  • go.mod (1 hunks)
  • lib/app/tasks.go (3 hunks)
  • lib/app/tasks_test.go (1 hunks)
  • lib/app/workflow.go (5 hunks)
  • lib/app/workflow_test.go (1 hunks)
  • lib/integrations/dotenv_vault/dotenv_vault.go (1 hunks)
  • lib/integrations/dotenv_vault/dotenv_vault_test.go (1 hunks)
  • lib/integrations/infisical/client.go (1 hunks)
  • lib/integrations/infisical/infiscal_test.go (1 hunks)
  • lib/integrations/infisical/infisical.go (1 hunks)
  • lib/integrations/integration.go (1 hunks)
  • lib/types/types.go (1 hunks)
💤 Files with no reviewable changes (1)
  • .github/workflows/build-and-test.yml
🧰 Additional context used
🧬 Code Graph Analysis (6)
lib/app/tasks.go (1)
lib/support/helpers.go (1)
  • SkippedMessageWithSymbol (16-18)
lib/integrations/integration.go (3)
lib/types/types.go (1)
  • AppWorkflowContract (44-49)
lib/integrations/dotenv_vault/dotenv_vault.go (1)
  • New (15-17)
lib/integrations/infisical/infisical.go (1)
  • New (17-19)
lib/integrations/infisical/infiscal_test.go (1)
lib/integrations/infisical/client.go (4)
  • Secret (34-49)
  • NewClient (20-25)
  • CreateSecretRequest (51-63)
  • UpdateSecretRequest (65-73)
lib/app/tasks_test.go (1)
lib/app/tasks.go (1)
  • Task (15-34)
lib/integrations/dotenv_vault/dotenv_vault_test.go (2)
lib/types/types.go (2)
  • JavaScriptEngineContract (25-35)
  • AppWorkflowContract (44-49)
lib/settings/settings.go (1)
  • Settings (5-16)
lib/integrations/dotenv_vault/dotenv_vault.go (2)
lib/types/types.go (1)
  • AppWorkflowContract (44-49)
lib/utils/utils.go (3)
  • ArrayContains (245-267)
  • IsFile (159-166)
  • WorkingDir (85-95)
🔇 Additional comments (29)
.github/codecov.yml (1)

1-4: Validate coverage precision and range settings
The precision: 2 and range: "70...100" look reasonable, but please confirm they align with your current coverage goals and reporting needs.

Taskfile.dist.yaml (1)

96-97: LGTM: Improved checksum generation process.

The changes make the checksum generation more explicit and cleaner by directly targeting the yaml files and removing path prefixes from the output. This enhances the template integrity checking process.

go.mod (1)

40-40: LGTM: Standard indirect dependency addition.

The addition of github.com/stretchr/objx v0.5.0 as an indirect dependency is appropriate for supporting the new integrations functionality.

lib/types/types.go (1)

48-48: LGTM: Clean interface extension for integrations.

The addition of GetEnvSection() []string method to the AppWorkflowContract interface is well-designed and follows Go conventions. It provides appropriate access to environment configuration for the new integrations functionality.

lib/app/tasks.go (3)

56-68: LGTM: Method exported for better testability.

Converting canRunOnCurrentPlatform to CanRunOnCurrentPlatform (exported) is a good improvement that enables direct testing of this platform compatibility logic.


70-76: LGTM: Method exported for better testability.

Converting canRunConditionally to CanRunConditionally (exported) enables direct testing of the conditional execution logic, improving test coverage.


166-174: LGTM: Consistent usage of exported methods.

The calls to the newly exported methods are updated correctly and maintain the existing functionality while enabling better testability.

lib/integrations/integration.go (2)

9-13: Well-designed integration interface.

The Integration interface follows good design principles with a clean, focused contract. The three methods provide a clear separation of concerns: identification (Name), conditional execution (IsEnabled), and the main functionality (Run).


15-23: Effective factory pattern with dependency injection.

The List function implements a clean factory pattern that:

  • Uses dependency injection via the getWorkflow function parameter, enabling flexible testing
  • Returns integrations keyed by their names for easy lookup
  • Centralizes integration instantiation logic

This design makes the integration system extensible and testable.

README.md (2)

44-44: Documentation properly updated.

The Infisical integration is correctly added to the table of contents in the appropriate section.


503-519: Comprehensive integration documentation.

The Infisical integration documentation is well-structured and includes:

  • Clear environment variable requirements (INFISICAL_API_TOKEN)
  • Practical configuration examples with the infisical://workspace-id:environment syntax
  • Important security note about service token access scope
  • Consistent formatting that matches other integration documentation

This provides users with all the necessary information to configure and use the integration effectively.

lib/app/workflow.go (7)

5-5: Import addition is justified.

The fmt import is correctly added to support the new error logging functionality in the integration execution loop.


16-16: Integration package import correctly added.

The integration package import enables the workflow to use the new integration system.


45-45: Integrations field properly added to workflow struct.

The Integrations field uses the correct type (map[string]integrations.Integration) to store integration instances keyed by their names, facilitating easy lookup and management.


53-67: Well-implemented workflow initialization.

The CreateWorkflow function properly:

  • Initializes the Integrations map as an empty map before population
  • Uses the new AsContract method for dependency injection
  • Calls integrations.List to populate the integrations map

This follows good initialization patterns and maintains clean separation of concerns.


69-71: Clean interface adapter method.

The AsContract method provides a clean way to convert the workflow to its contract interface, supporting dependency injection and interface segregation principles.


97-99: Method correctly implements interface contract.

The GetEnvSection method satisfies the AppWorkflowContract interface requirement and provides access to the workflow's environment configuration.


129-137: Improved integration execution with proper error handling.

The new integration execution loop:

  • Iterates through all available integrations
  • Checks if each integration is enabled before running
  • Includes proper error handling with descriptive error messages
  • Replaces the previous single dotenv-vault call with a more extensible approach

This design makes the system more modular and provides better visibility into integration failures.

lib/app/workflow_test.go (3)

12-25: Comprehensive workflow creation test.

The TestCreateWorkflow function properly verifies that all workflow fields are initialized, including the new Integrations field. This ensures the refactoring doesn't break existing functionality and that new fields are properly set up.


27-32: Contract conversion test covers new functionality.

The TestAsContract test verifies that the new AsContract method works correctly, ensuring the workflow can be properly converted to its interface contract for dependency injection.


34-50: Thorough task lookup test coverage.

The TestFindTaskById function provides comprehensive coverage with:

  • Positive case: finding an existing task
  • Negative case: attempting to find a non-existent task
  • Type assertion verification for the returned interface

This ensures the task lookup functionality works correctly after the interface changes.

lib/app/tasks_test.go (2)

21-66: Excellent test coverage for GetDisplayName method.

The test cases comprehensively cover the priority logic: Include (with URL processing) → Name → Id → Uuid → empty string. The test scenarios are well-structured and validate the expected fallback behavior.


68-86: Good platform compatibility test coverage.

The tests properly cover all edge cases including nil/empty platforms list and case-sensitive platform matching using runtime.GOOS.

lib/integrations/dotenv_vault/dotenv_vault.go (3)

11-17: Well-designed integration structure.

The use of dependency injection with the workflow function parameter enables clean testing and loose coupling. The constructor pattern is appropriate for this integration.


23-25: Efficient integration enablement check.

The implementation correctly uses utils.ArrayContains to check for the "dotenv://vault" marker in the environment section, providing a clean way to enable/disable the integration.


27-46: Robust Run method implementation.

The method has good defensive programming with early returns when disabled or when the vault file doesn't exist. Error handling from godotenvvault.Read() is properly propagated, and environment variables are correctly set using os.Setenv.

lib/integrations/dotenv_vault/dotenv_vault_test.go (3)

80-84: Simple and effective Name method test.

The test correctly verifies that the integration returns the expected name "dotenv-vault".


87-104: Good IsEnabled method test coverage.

The tests properly verify both enabled and disabled states by checking the presence of "dotenv://vault" in the environment section.


30-43: Appropriate use of panic for unimplemented mock methods.

Using panic("unimplemented") for unused interface methods in test mocks is acceptable since these methods aren't exercised in the current tests. This follows the principle of implementing only what's needed for testing.

Comment on lines +10 to +13
patch:
default:
target: 30%
threshold: 30%
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Quote patch-level threshold to avoid YAML parsing errors
Similarly, the unquoted 30% can break the YAML loader. Please wrap it in quotes.

--- a/.github/codecov.yml
+++ b/.github/codecov.yml
@@ -12,7 +12,7 @@ status:
       default:
         target: 30%
-        threshold: 30%
+        threshold: "30%"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
patch:
default:
target: 30%
threshold: 30%
patch:
default:
target: 30%
threshold: "30%"
🤖 Prompt for AI Agents
In .github/codecov.yml around lines 10 to 13, the patch-level threshold value
30% is unquoted, which can cause YAML parsing errors. To fix this, wrap the
threshold value 30% in double quotes to ensure it is parsed correctly as a
string.

Comment on lines +5 to +9
status:
project:
default:
target: auto
threshold: 15%
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Quote project-level threshold to avoid YAML parsing errors
Unquoted 15% may not be parsed correctly by YAML. Wrap it in quotes to ensure it’s interpreted as a string.
Apply this diff:

--- a/.github/codecov.yml
+++ b/.github/codecov.yml
@@ -8,7 +8,7 @@ status:
       default:
         target: auto
-        threshold: 15%
+        threshold: "15%"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
status:
project:
default:
target: auto
threshold: 15%
status:
project:
default:
target: auto
threshold: "15%"
🤖 Prompt for AI Agents
In .github/codecov.yml around lines 5 to 9, the project-level threshold value
15% is unquoted, which can cause YAML parsing errors. Fix this by wrapping the
threshold value in quotes, changing it from 15% to "15%".

Comment on lines +88 to +123
// func TestCanRunConditionally(t *testing.T) {
// assert := assert.New(t)

// // Test case: When If field is empty
// task1 := &app.Task{If: ""}
// assert.True(task1.CanRunConditionally())

// // Test case: When JsEngine.Evaluate returns true
// getJsEngine := func(mockEval func(script string) interface{}) interface{} {
// engine := &MockJsEngine{
// MockEvaluate: mockEval,
// }

// return engine
// }

// jsengine2 := getJsEngine(func(script string) interface{} {
// return true
// })

// task2 := &app.Task{
// If: "{{ some condition }}",
// JsEngine: jsengine2.(*scripting.JavaScriptEngine),
// }
// assert.True(task2.CanRunConditionally())

// // Test case: When JsEngine.Evaluate returns false
// jsengine3 := getJsEngine(func(script string) interface{} {
// return false
// })
// task3 := &app.Task{
// If: "some condition",
// JsEngine: jsengine3.(*scripting.JavaScriptEngine),
// }
// assert.False(task3.CanRunConditionally())
// }
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix type casting issue in commented test.

The commented test has a type casting issue on line 110 that would prevent compilation. The getJsEngine function returns interface{} but tries to cast to *scripting.JavaScriptEngine.

If you plan to uncomment this test, fix the type issue:

-// 	task2 := &app.Task{
-// 		If:       "{{ some condition }}",
-// 		JsEngine: jsengine2.(*scripting.JavaScriptEngine),
-// 	}
+// 	task2 := &app.Task{
+// 		If:       "{{ some condition }}",
+// 		JsEngine: jsengine2.(*MockJsEngine),
+// 	}

However, note that this would still not work since JsEngine expects *scripting.JavaScriptEngine, not *MockJsEngine. Consider creating a proper interface or adjusting the test design.

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

🤖 Prompt for AI Agents
In lib/app/tasks_test.go between lines 88 and 123, the commented test has a type
casting issue where getJsEngine returns interface{} but the code casts it to
*scripting.JavaScriptEngine, which is incorrect since the returned object is
actually *MockJsEngine. To fix this, either change getJsEngine to return
*scripting.JavaScriptEngine or adjust the test to use an interface that both
*MockJsEngine and *scripting.JavaScriptEngine implement, then assign JsEngine to
that interface type. This will resolve the type mismatch and allow the test to
compile and run correctly.

Comment on lines +12 to +19
type MockJsEngine struct {
MockEvaluate func(script string) interface{}
mock.Mock
}

func (m *MockJsEngine) Evaluate(script string) interface{} {
return m.MockEvaluate(script)
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Fix MockJsEngine implementation inconsistency.

The MockJsEngine struct embeds mock.Mock but uses a custom MockEvaluate function field instead of leveraging the mock framework properly. This creates confusion and doesn't follow testify best practices.

Choose one of these approaches:

Option 1: Use testify mock framework properly:

 type MockJsEngine struct {
-	MockEvaluate func(script string) interface{}
 	mock.Mock
 }

 func (m *MockJsEngine) Evaluate(script string) interface{} {
-	return m.MockEvaluate(script)
+	args := m.Called(script)
+	return args.Get(0)
 }

Option 2: Use simple function field approach:

 type MockJsEngine struct {
 	MockEvaluate func(script string) interface{}
-	mock.Mock
 }
🤖 Prompt for AI Agents
In lib/app/tasks_test.go around lines 12 to 19, the MockJsEngine struct
inconsistently mixes testify's mock.Mock embedding with a custom MockEvaluate
function field, which is confusing and not aligned with testify best practices.
To fix this, either remove the MockEvaluate field and implement Evaluate by
calling m.Called(script).Get(0) to use testify's mocking properly, or remove the
mock.Mock embedding and keep only the function field for a simpler approach.
Choose one consistent mocking strategy and update the Evaluate method
accordingly.

Comment on lines +11 to +29
func TestGetSecrets(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
secrets := []Secret{
{Name: "test-secret", Value: "test-value"},
}
json.NewEncoder(w).Encode(map[string][]Secret{"secrets": secrets})
}))
defer server.Close()

client := NewClient(server.URL)
secrets, err := client.GetSecrets("workspaceId", "environment", "path", false)
if err != nil {
t.Fatalf("Expected no error, got %v", err)
}

if len(secrets) != 1 {
t.Fatalf("Expected 1 secret, got %d", len(secrets))
}
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Good basic test structure for GetSecrets method.

The test properly mocks the HTTP server and validates basic JSON response parsing. The test structure is clean and follows good testing practices.

Consider adding error case testing:

func TestGetSecretsError(t *testing.T) {
    server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusInternalServerError)
    }))
    defer server.Close()
    
    client := NewClient(server.URL)
    _, err := client.GetSecrets("workspaceId", "environment", "path", false)
    if err == nil {
        t.Fatal("Expected error for 500 status code")
    }
}
🤖 Prompt for AI Agents
In lib/integrations/infisical/infiscal_test.go around lines 11 to 29, the
existing TestGetSecrets function tests the successful case but lacks coverage
for error scenarios. Add a new test function named TestGetSecretsError that
mocks the HTTP server to return a 500 Internal Server Error status. In this
test, call client.GetSecrets and assert that it returns an error, failing the
test if no error is returned. This will ensure error handling paths are properly
tested.

Comment on lines +166 to +169
// body := make([]byte, 81920)
// resp.Body.Read(body)
// fmt.Printf("resp.Body: %v\n", string(body))

Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick (assertive)

Remove commented debug code.

Debug code should not be included in production code.

-	// body := make([]byte, 81920)
-	// resp.Body.Read(body)
-	// fmt.Printf("resp.Body: %v\n", string(body))
-
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// body := make([]byte, 81920)
// resp.Body.Read(body)
// fmt.Printf("resp.Body: %v\n", string(body))
🤖 Prompt for AI Agents
In lib/integrations/infisical/client.go around lines 166 to 169, remove the
commented-out debug code that reads from resp.Body and prints it. This debug
code is not needed in production and should be deleted to keep the code clean.

Comment on lines +231 to +233
func (c *Client) GetSecret(workspaceId, environment, secretName, path string) (Secret, error) {
endpoint := "/api/v3/secrets/" + secretName + "?workspaceId=" + workspaceId + "&environment=" + environment + "&path=" + path

Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Apply URL encoding to prevent injection in other endpoints.

Similar to GetSecrets, other methods that build URLs should use proper encoding.

Run the following script to verify all URL construction patterns in the codebase:

Also applies to: 291-293


🏁 Script executed:

#!/bin/bash
# Description: Find all instances of URL construction to ensure proper encoding is used

# Search for potential URL construction patterns
rg -A 3 'endpoint.*"\/api\/.*\+' --type go

# Search for methods that might construct URLs
ast-grep --pattern 'endpoint := $_ + $_'

Length of output: 858


Apply proper URL encoding for all path segments and query parameters

We’re currently concatenating raw IDs and names into endpoints, which can lead to injection or malformed‐URL issues. Please update every URL construction in lib/integrations/infisical/client.go to use Go’s net/url escaping functions:

• lib/integrations/infisical/client.go:

  • Line 151 (GetSecrets)
  • Line 232 (GetSecret)
  • Lines 252 & 272 (DeleteSecret / RestoreSecret)
  • Line 292 (UpdateSecret or similar)

Example diff for GetSecret (line 232):

- endpoint := "/api/v3/secrets/" + secretName + "?workspaceId=" + workspaceId + "&environment=" + environment + "&path=" + path
+ // URL‐escape path segments and query values
+ endpoint := fmt.Sprintf(
+   "/api/v3/secrets/%s?workspaceId=%s&environment=%s&path=%s",
+   url.PathEscape(secretName),
+   url.QueryEscape(workspaceId),
+   url.QueryEscape(environment),
+   url.QueryEscape(path),
+ )

Repeat this pattern for each method. Alternatively, consider constructing URLs with url.URL and url.Values for cleaner code and automatic encoding.

🤖 Prompt for AI Agents
In lib/integrations/infisical/client.go around lines 231 to 233, the URL for the
GetSecret method is constructed by concatenating raw strings, which risks
injection and malformed URLs. Fix this by applying proper URL encoding to all
path segments and query parameters using Go's net/url package functions such as
url.PathEscape for path elements and url.QueryEscape or url.Values for query
parameters. Repeat this encoding approach for all similar URL constructions in
the file at lines 151, 252, 272, and 292 to ensure consistent and safe URL
building.

Comment on lines +175 to +210
result := secretsResponse.Secrets
token := os.Getenv("INFISICAL_API_TOKEN")

lastIndex := strings.LastIndex(token, ".")
tokenSecret := ""

if lastIndex != -1 && lastIndex+1 < len(token) {
tokenSecret = token[lastIndex+1:]
}

tokenData, _ := c.GetTokenData(workspaceId, environment)

secretKey, err := decrypt(tokenData.EncryptedKey, tokenData.Iv, tokenData.Tag, tokenSecret)
if err != nil {
return nil, err
}

for _, secret := range result {
if secret.SecretKeyCiphertext != "" {
plainTextKey, err := decrypt(secret.SecretKeyCiphertext, secret.SecretKeyIV, secret.SecretKeyTag, secretKey)
if err != nil {
return nil, err
}

plainTextValue, err := decrypt(secret.SecretValueCiphertext, secret.SecretValueIV, secret.SecretValueTag, secretKey)
if err != nil {
return nil, err
}

secret.Name = plainTextKey
secret.Value = plainTextValue
}
}

return result, nil
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix mutation of secrets and improve error handling.

The code attempts to mutate secret objects within a range loop, but since Go passes by value, these mutations won't affect the original slice. Additionally, there's no validation of the token format.

Fix the mutation issue and add token validation:

 	result := secretsResponse.Secrets
 	token := os.Getenv("INFISICAL_API_TOKEN")
+	if token == "" {
+		return nil, errors.New("INFISICAL_API_TOKEN is not set")
+	}
 
 	lastIndex := strings.LastIndex(token, ".")
 	tokenSecret := ""
 
 	if lastIndex != -1 && lastIndex+1 < len(token) {
 		tokenSecret = token[lastIndex+1:]
+	} else {
+		return nil, errors.New("invalid token format")
 	}
 
 	tokenData, _ := c.GetTokenData(workspaceId, environment)
 
 	secretKey, err := decrypt(tokenData.EncryptedKey, tokenData.Iv, tokenData.Tag, tokenSecret)
 	if err != nil {
 		return nil, err
 	}
 
-	for _, secret := range result {
+	for i := range result {
+		secret := &result[i]
 		if secret.SecretKeyCiphertext != "" {
 			plainTextKey, err := decrypt(secret.SecretKeyCiphertext, secret.SecretKeyIV, secret.SecretKeyTag, secretKey)
 			if err != nil {
 				return nil, err
 			}
 
 			plainTextValue, err := decrypt(secret.SecretValueCiphertext, secret.SecretValueIV, secret.SecretValueTag, secretKey)
 			if err != nil {
 				return nil, err
 			}
 
 			secret.Name = plainTextKey
 			secret.Value = plainTextValue
 		}
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
result := secretsResponse.Secrets
token := os.Getenv("INFISICAL_API_TOKEN")
lastIndex := strings.LastIndex(token, ".")
tokenSecret := ""
if lastIndex != -1 && lastIndex+1 < len(token) {
tokenSecret = token[lastIndex+1:]
}
tokenData, _ := c.GetTokenData(workspaceId, environment)
secretKey, err := decrypt(tokenData.EncryptedKey, tokenData.Iv, tokenData.Tag, tokenSecret)
if err != nil {
return nil, err
}
for _, secret := range result {
if secret.SecretKeyCiphertext != "" {
plainTextKey, err := decrypt(secret.SecretKeyCiphertext, secret.SecretKeyIV, secret.SecretKeyTag, secretKey)
if err != nil {
return nil, err
}
plainTextValue, err := decrypt(secret.SecretValueCiphertext, secret.SecretValueIV, secret.SecretValueTag, secretKey)
if err != nil {
return nil, err
}
secret.Name = plainTextKey
secret.Value = plainTextValue
}
}
return result, nil
}
result := secretsResponse.Secrets
token := os.Getenv("INFISICAL_API_TOKEN")
if token == "" {
return nil, errors.New("INFISICAL_API_TOKEN is not set")
}
lastIndex := strings.LastIndex(token, ".")
tokenSecret := ""
if lastIndex != -1 && lastIndex+1 < len(token) {
tokenSecret = token[lastIndex+1:]
} else {
return nil, errors.New("invalid token format")
}
tokenData, _ := c.GetTokenData(workspaceId, environment)
secretKey, err := decrypt(tokenData.EncryptedKey, tokenData.Iv, tokenData.Tag, tokenSecret)
if err != nil {
return nil, err
}
for i := range result {
secret := &result[i]
if secret.SecretKeyCiphertext != "" {
plainTextKey, err := decrypt(secret.SecretKeyCiphertext, secret.SecretKeyIV, secret.SecretKeyTag, secretKey)
if err != nil {
return nil, err
}
plainTextValue, err := decrypt(secret.SecretValueCiphertext, secret.SecretValueIV, secret.SecretValueTag, secretKey)
if err != nil {
return nil, err
}
secret.Name = plainTextKey
secret.Value = plainTextValue
}
}
return result, nil
}
🤖 Prompt for AI Agents
In lib/integrations/infisical/client.go lines 175 to 210, the code mutates
secret objects inside a range loop, but since Go passes loop variables by value,
these changes do not affect the original slice. To fix this, iterate using index
and update the slice elements directly. Also, add validation to ensure the token
is not empty and has the expected format before extracting the tokenSecret.
Return an error if the token is invalid.

Comment on lines +75 to +111
func (c *Client) makeRequest(method, endpoint string, payload interface{}) (*http.Response, error) {
url := c.APIEndpoint + endpoint
var req *http.Request
var err error

if method == http.MethodGet || method == http.MethodDelete {
req, err = http.NewRequest(method, url, nil)
} else {
jsonPayload, _ := json.Marshal(payload)
req, err = http.NewRequest(method, url, bytes.NewBuffer(jsonPayload))
req.Header.Set("Content-Type", "application/json")
}

if err != nil {
return nil, err
}

token := os.Getenv("INFISICAL_API_TOKEN")
apiKey := os.Getenv("INFISICAL_API_KEY")

req.Header.Set("User-Agent", "StackUp-cli/v1")

if token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}

if apiKey != "" {
req.Header.Del("Authorization")
req.Header.Set("X-API-KEY", apiKey)
}

if apiKey == "" && token == "" {
return nil, errors.New("INFISICAL_API_KEY or INFISICAL_API_TOKEN environment variable must be set")
}

return c.HTTPClient.Do(req)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Improve error handling in makeRequest method.

Several issues need attention:

  1. The error from json.Marshal is ignored
  2. Authentication validation should happen at the beginning
  3. Setting API key header removes Authorization header unnecessarily

Apply these improvements:

 func (c *Client) makeRequest(method, endpoint string, payload interface{}) (*http.Response, error) {
+	token := os.Getenv("INFISICAL_API_TOKEN")
+	apiKey := os.Getenv("INFISICAL_API_KEY")
+	
+	if apiKey == "" && token == "" {
+		return nil, errors.New("INFISICAL_API_KEY or INFISICAL_API_TOKEN environment variable must be set")
+	}
+	
 	url := c.APIEndpoint + endpoint
 	var req *http.Request
 	var err error
 
 	if method == http.MethodGet || method == http.MethodDelete {
 		req, err = http.NewRequest(method, url, nil)
 	} else {
-		jsonPayload, _ := json.Marshal(payload)
+		jsonPayload, err := json.Marshal(payload)
+		if err != nil {
+			return nil, err
+		}
 		req, err = http.NewRequest(method, url, bytes.NewBuffer(jsonPayload))
 		req.Header.Set("Content-Type", "application/json")
 	}
 
 	if err != nil {
 		return nil, err
 	}
 
-	token := os.Getenv("INFISICAL_API_TOKEN")
-	apiKey := os.Getenv("INFISICAL_API_KEY")
-
 	req.Header.Set("User-Agent", "StackUp-cli/v1")
 
-	if token != "" {
-		req.Header.Set("Authorization", "Bearer "+token)
-	}
-
 	if apiKey != "" {
-		req.Header.Del("Authorization")
 		req.Header.Set("X-API-KEY", apiKey)
-	}
-
-	if apiKey == "" && token == "" {
-		return nil, errors.New("INFISICAL_API_KEY or INFISICAL_API_TOKEN environment variable must be set")
+	} else if token != "" {
+		req.Header.Set("Authorization", "Bearer "+token)
 	}
 
 	return c.HTTPClient.Do(req)
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
func (c *Client) makeRequest(method, endpoint string, payload interface{}) (*http.Response, error) {
url := c.APIEndpoint + endpoint
var req *http.Request
var err error
if method == http.MethodGet || method == http.MethodDelete {
req, err = http.NewRequest(method, url, nil)
} else {
jsonPayload, _ := json.Marshal(payload)
req, err = http.NewRequest(method, url, bytes.NewBuffer(jsonPayload))
req.Header.Set("Content-Type", "application/json")
}
if err != nil {
return nil, err
}
token := os.Getenv("INFISICAL_API_TOKEN")
apiKey := os.Getenv("INFISICAL_API_KEY")
req.Header.Set("User-Agent", "StackUp-cli/v1")
if token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}
if apiKey != "" {
req.Header.Del("Authorization")
req.Header.Set("X-API-KEY", apiKey)
}
if apiKey == "" && token == "" {
return nil, errors.New("INFISICAL_API_KEY or INFISICAL_API_TOKEN environment variable must be set")
}
return c.HTTPClient.Do(req)
}
func (c *Client) makeRequest(method, endpoint string, payload interface{}) (*http.Response, error) {
token := os.Getenv("INFISICAL_API_TOKEN")
apiKey := os.Getenv("INFISICAL_API_KEY")
if apiKey == "" && token == "" {
return nil, errors.New("INFISICAL_API_KEY or INFISICAL_API_TOKEN environment variable must be set")
}
url := c.APIEndpoint + endpoint
var req *http.Request
var err error
if method == http.MethodGet || method == http.MethodDelete {
req, err = http.NewRequest(method, url, nil)
} else {
jsonPayload, err := json.Marshal(payload)
if err != nil {
return nil, err
}
req, err = http.NewRequest(method, url, bytes.NewBuffer(jsonPayload))
req.Header.Set("Content-Type", "application/json")
}
if err != nil {
return nil, err
}
req.Header.Set("User-Agent", "StackUp-cli/v1")
if apiKey != "" {
req.Header.Set("X-API-KEY", apiKey)
} else if token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}
return c.HTTPClient.Do(req)
}
🤖 Prompt for AI Agents
In lib/integrations/infisical/client.go lines 75 to 111, improve error handling
in makeRequest by first validating that either INFISICAL_API_KEY or
INFISICAL_API_TOKEN is set before creating the request. Then, capture and handle
the error returned by json.Marshal instead of ignoring it. Finally, when setting
the API key header, do not remove the Authorization header unnecessarily;
instead, set headers based on which credential is present without deleting
others arbitrarily.

Comment on lines +150 to +155
func (c *Client) GetSecrets(workspaceId, environment, path string, includeImports bool) ([]Secret, error) {
endpoint := "/api/v3/secrets?workspaceId=" + workspaceId + "&environment=" + environment + "&path=" + path
if includeImports {
endpoint += "&include_imports=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

Fix potential URL injection vulnerability.

Building URLs by string concatenation without proper encoding can lead to URL injection attacks.

Use URL encoding to prevent injection:

+import (
+	"net/url"
+	// ... other imports
+)
+
 func (c *Client) GetSecrets(workspaceId, environment, path string, includeImports bool) ([]Secret, error) {
-	endpoint := "/api/v3/secrets?workspaceId=" + workspaceId + "&environment=" + environment + "&path=" + path
+	params := url.Values{}
+	params.Add("workspaceId", workspaceId)
+	params.Add("environment", environment)
+	params.Add("path", path)
 	if includeImports {
-		endpoint += "&include_imports=true"
+		params.Add("include_imports", "true")
 	}
+	endpoint := "/api/v3/secrets?" + params.Encode()
🤖 Prompt for AI Agents
In lib/integrations/infisical/client.go around lines 150 to 155, the URL is
constructed by concatenating query parameters directly, which risks URL
injection. Fix this by using proper URL encoding for the workspaceId,
environment, and path parameters before appending them to the endpoint string.
Use a URL encoding function to safely encode these values to prevent injection
vulnerabilities.

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