Skip to content

Refactor 2 - #15

Open
patinthehat wants to merge 17 commits into
mainfrom
refactor-2
Open

Refactor 2#15
patinthehat wants to merge 17 commits into
mainfrom
refactor-2

Conversation

@patinthehat

@patinthehatpatinthehat commented Sep 15, 2023

Copy link
Copy Markdown
Member

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
Copy Markdown

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

codecovBot commented Sep 15, 2023

Copy link
Copy Markdown

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 ChangedCoverage Δ
lib/app/tasks.go12.27% <25.00%> (+12.27%)⬆️
lib/app/workflow.go11.74% <44.44%> (+11.74%)⬆️

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

@patinthehat

Copy link
Copy Markdown
MemberAuthor

@coderabbitai summary

@coderabbitai

Copy link
Copy Markdown
✅ Actions performed

Summary regeneration triggered.

@coderabbitai

coderabbitaiBot commented Jun 9, 2025

Copy link
Copy Markdown

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.ymlAdded Codecov config for coverage precision, rounding, and status thresholds.
.github/workflows/build-and-test.ymlRemoved pull_request_target trigger; simplified event triggers.
README.mdDocumented Infisical integration, usage, and example; updated ToC.
Taskfile.dist.yamlRefactored update-checksums task: explicit paths, path prefix trimming, removed git commands.
go.modAdded indirect dependency on github.com/stretchr/objx v0.5.0.
lib/app/tasks.goExported CanRunOnCurrentPlatform and CanRunConditionally methods.
lib/app/tasks_test.goAdded tests for Task methods using a mock JS engine.
lib/app/workflow.goAdded Integrations map, methods for contract/env section, and integration initialization logic.
lib/app/workflow_test.goAdded tests for workflow creation, contract, and task lookup.
lib/integrations/dotenv_vault/dotenv_vault.goAdded dotenv-vault integration: loads .env.vault if enabled.
lib/integrations/dotenv_vault/dotenv_vault_test.goAdded tests and mocks for dotenv-vault integration.
lib/integrations/infisical/client.goAdded Infisical API client: supports secret CRUD, decryption, and token management.
lib/integrations/infisical/infiscal_test.goAdded tests for Infisical client: covers all major API methods with mock server.
lib/integrations/infisical/infisical.goAdded Infisical integration: parses env entries, fetches secrets, injects into environment.
lib/integrations/integration.goIntroduced Integration interface and registry for dotenv-vault and Infisical.
lib/types/types.goAdded 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
Copy Markdown
MemberAuthor

@CodeRabbit full review

@coderabbitai

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown

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 thread.github/codecov.yml
Comment on lines +10 to +13
patch:
default:
target: 30%
threshold: 30%

Copy link
Copy Markdown

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 thread.github/codecov.yml
Comment on lines +5 to +9
status:
project:
default:
target: auto
threshold: 15%

Copy link
Copy Markdown

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 threadlib/app/tasks_test.go
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
Copy Markdown

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 threadlib/app/tasks_test.go
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
Copy Markdown

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
Copy Markdown

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:

funcTestGetSecretsError(t*testing.T) {
server:=httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r*http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
deferserver.Close()
client:=NewClient(server.URL)
_, err:=client.GetSecrets("workspaceId", "environment", "path", false)
iferr==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
Copy Markdown

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
Copy Markdown

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
Copy Markdown

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:=""
iflastIndex!=-1&&lastIndex+1<len(token) {
tokenSecret=token[lastIndex+1:]
}
tokenData, _:=c.GetTokenData(workspaceId, environment)
secretKey, err:=decrypt(tokenData.EncryptedKey, tokenData.Iv, tokenData.Tag, tokenSecret)
iferr!=nil {
returnnil, err
}
for_, secret:=rangeresult {
ifsecret.SecretKeyCiphertext!="" {
plainTextKey, err:=decrypt(secret.SecretKeyCiphertext, secret.SecretKeyIV, secret.SecretKeyTag, secretKey)
iferr!=nil {
returnnil, err
}
plainTextValue, err:=decrypt(secret.SecretValueCiphertext, secret.SecretValueIV, secret.SecretValueTag, secretKey)
iferr!=nil {
returnnil, err
}
secret.Name=plainTextKey
secret.Value=plainTextValue
}
}
returnresult, nil
}
result:=secretsResponse.Secrets
token:=os.Getenv("INFISICAL_API_TOKEN")
iftoken=="" {
returnnil, errors.New("INFISICAL_API_TOKEN is not set")
}
lastIndex:=strings.LastIndex(token, ".")
tokenSecret:=""
iflastIndex!=-1&&lastIndex+1<len(token) {
tokenSecret=token[lastIndex+1:]
} else {
returnnil, errors.New("invalid token format")
}
tokenData, _:=c.GetTokenData(workspaceId, environment)
secretKey, err:=decrypt(tokenData.EncryptedKey, tokenData.Iv, tokenData.Tag, tokenSecret)
iferr!=nil {
returnnil, err
}
fori:=rangeresult {
secret:=&result[i]
ifsecret.SecretKeyCiphertext!="" {
plainTextKey, err:=decrypt(secret.SecretKeyCiphertext, secret.SecretKeyIV, secret.SecretKeyTag, secretKey)
iferr!=nil {
returnnil, err
}
plainTextValue, err:=decrypt(secret.SecretValueCiphertext, secret.SecretValueIV, secret.SecretValueTag, secretKey)
iferr!=nil {
returnnil, err
}
secret.Name=plainTextKey
secret.Value=plainTextValue
}
}
returnresult, 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
Copy Markdown

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, endpointstring, payloadinterface{}) (*http.Response, error) {
url:=c.APIEndpoint+endpoint
varreq*http.Request
varerrerror
ifmethod==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")
}
iferr!=nil {
returnnil, err
}
token:=os.Getenv("INFISICAL_API_TOKEN")
apiKey:=os.Getenv("INFISICAL_API_KEY")
req.Header.Set("User-Agent", "StackUp-cli/v1")
iftoken!="" {
req.Header.Set("Authorization", "Bearer "+token)
}
ifapiKey!="" {
req.Header.Del("Authorization")
req.Header.Set("X-API-KEY", apiKey)
}
ifapiKey==""&&token=="" {
returnnil, errors.New("INFISICAL_API_KEY or INFISICAL_API_TOKEN environment variable must be set")
}
returnc.HTTPClient.Do(req)
}
func (c*Client) makeRequest(method, endpointstring, payloadinterface{}) (*http.Response, error) {
token:=os.Getenv("INFISICAL_API_TOKEN")
apiKey:=os.Getenv("INFISICAL_API_KEY")
ifapiKey==""&&token=="" {
returnnil, errors.New("INFISICAL_API_KEY or INFISICAL_API_TOKEN environment variable must be set")
}
url:=c.APIEndpoint+endpoint
varreq*http.Request
varerrerror
ifmethod==http.MethodGet||method== http.MethodDelete {
req,err=http.NewRequest(method, url, nil)
} else {
jsonPayload, err:=json.Marshal(payload)
iferr!=nil {
returnnil, err
}
req, err=http.NewRequest(method, url, bytes.NewBuffer(jsonPayload))
req.Header.Set("Content-Type", "application/json")
}
iferr!=nil {
returnnil, err
}
req.Header.Set("User-Agent", "StackUp-cli/v1")
ifapiKey!="" {
req.Header.Set("X-API-KEY", apiKey)
} elseiftoken!="" {
req.Header.Set("Authorization", "Bearer "+token)
}
returnc.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
Copy Markdown

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 freeto 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

@patinthehat