Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
21a2c63
redid keys logic, now the user can specify the model he wants to use
Mariola04 Jan 8, 2026
673f228
lint errors
Mariola04 Jan 8, 2026
79de253
.
Mariola04 Jan 8, 2026
e80b98f
fotgot to change targer branch
Mariola04 Jan 8, 2026
b9c889e
test
Mariola04 Jan 8, 2026
99c430b
test with gemini to check key variance
Mariola04 Jan 8, 2026
06ed433
test gemini
Mariola04 Jan 8, 2026
bbc705a
test gemini again
Mariola04 Jan 8, 2026
fa305fd
test gemini new key
Mariola04 Jan 8, 2026
4ab99dd
trig
JocaSantos-dev Jan 8, 2026
32a1c62
test
Mariola04 Jan 8, 2026
f1b6248
Merge branch 'keys-changes' of https://github.com/Scalabit/workflow-s…
Mariola04 Jan 8, 2026
35a0620
test
Mariola04 Jan 8, 2026
b31cd49
test
JocaSantos-dev Jan 8, 2026
4f73790
trig
JocaSantos-dev Jan 8, 2026
ea91472
debug
JocaSantos-dev Jan 8, 2026
91664f0
test2
JocaSantos-dev Jan 8, 2026
d2c2c2e
fix: text was all in header mode
JocaSantos-dev Jan 8, 2026
b2fed08
fix2
JocaSantos-dev Jan 9, 2026
dcbf149
test3
JocaSantos-dev Jan 9, 2026
0374ef5
test4
JocaSantos-dev Jan 9, 2026
ccd9320
test5
JocaSantos-dev Jan 9, 2026
6646e13
test6
JocaSantos-dev Jan 9, 2026
672883d
feat: Improve description on autofixes
JocaSantos-dev Jan 9, 2026
c32a0d2
feat: refactor external deps pr visual
JocaSantos-dev Jan 9, 2026
3c34441
test manual reviews with collapsiles
JocaSantos-dev Jan 9, 2026
45b826a
feat: improve external deps visual on the pr
JocaSantos-dev Jan 9, 2026
9632aed
feat: add expand comment info
JocaSantos-dev Jan 9, 2026
ec4fff6
feat: fix UT with last changes
JocaSantos-dev Jan 12, 2026
ff5a694
fix: UT main test
JocaSantos-dev Jan 12, 2026
fa70f0f
fix typo
JocaSantos-dev Jan 12, 2026
2d223b7
fix: resolve import cycle in pkg/zizmor and regenerate mocks
JocaSantos-dev Jan 13, 2026
ed75698
fix: update mockgen path to generate mock in same package
JocaSantos-dev Jan 13, 2026
7e65e46
refactor: agent testing, remove dagger dependency
JocaSantos-dev Jan 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .github/workflows/workflow-scanner.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
name: Test Workflow Scanner

on:
pull_request:
push:
branches:
- improve-pr

jobs:
test-scanner:
Expand All @@ -28,5 +30,5 @@ jobs:
with:
api-token: ${{ secrets.FS_API_TOKEN }}
github-token: ${{ secrets.GH_PAT }}
llm-api-key: ${{ secrets.GEMINI_API_KEY }}
target-branch: test-locally
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
target-branch: improve-pr
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,4 @@ This project is licensed under the terms included in the LICENSE file.
## Next steps
- See if Docker image + entrypoint script, instead of composite, can be better.
- Don't make this repo public until we remove the LLM KEY and PAT from secrets.
- See what are the possibilities of using GITHUB_TOKEN instead PAT_TOKEN.
- See what are the possibilities of using GITHUB_TOKEN instead PAT_TOKEN.
101 changes: 66 additions & 35 deletions cmd/scanner/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ type batchConfig struct {
provider string
githubToken string
gitlabToken string
llmAPIKey string
openaiKey string
anthropicKey string
geminiKey string
model string
commitSHA string
sourceBase64 string
useGitClone bool
Expand All @@ -42,7 +45,6 @@ func main() {
config.repository, config.commitSHA, config.useGitClone)

validateConfig(config)
setupLLMEnvironment(config.llmAPIKey)
validateDaggerEnvironment()

ctx := context.Background()
Expand Down Expand Up @@ -71,11 +73,24 @@ func loadConfig() batchConfig {
provider := strings.ToLower(os.Getenv("PROVIDER")) // optional override
githubToken := os.Getenv("GITHUB_TOKEN")
gitlabToken := os.Getenv("GITLAB_TOKEN")
llmAPIKey := os.Getenv("LLM_API_KEY")
openaiKey := os.Getenv("OPENAI_API_KEY")
anthropicKey := os.Getenv("ANTHROPIC_API_KEY")
geminiKey := os.Getenv("GEMINI_API_KEY")
model := os.Getenv("MODEL")
commitSHA := os.Getenv("COMMIT_SHA")
sourceBase64 := os.Getenv("SOURCE_BASE64")

useGitClone := sourceBase64 == "" && llmAPIKey != ""
// Check if any LLM key is available
hasLLMKey := openaiKey != "" || anthropicKey != "" || geminiKey != ""
useGitClone := sourceBase64 == "" && hasLLMKey

// Validate API key formats to catch user errors early
validateAPIKeyFormats(openaiKey, anthropicKey, geminiKey)

// Set default model based on available API key if not specified
if model == "" {
model = getDefaultModel(openaiKey, anthropicKey, geminiKey)
}

if provider == "" {
if strings.Contains(repository, "gitlab.com") {
Expand All @@ -90,18 +105,60 @@ func loadConfig() batchConfig {
provider: provider,
githubToken: githubToken,
gitlabToken: gitlabToken,
llmAPIKey: llmAPIKey,
openaiKey: openaiKey,
anthropicKey: anthropicKey,
geminiKey: geminiKey,
model: model,
commitSHA: commitSHA,
sourceBase64: sourceBase64,
useGitClone: useGitClone,
}
}

func validateAPIKeyFormats(openaiKey, anthropicKey, geminiKey string) {
if openaiKey != "" && !strings.HasPrefix(openaiKey, "sk-") {
log.Fatal("OPENAI_API_KEY appears to be invalid format (should start with 'sk-')")
}
if anthropicKey != "" && !strings.HasPrefix(anthropicKey, "sk-ant-") {
log.Fatal("ANTHROPIC_API_KEY appears to be invalid format (should start with 'sk-ant-')")
}
if geminiKey != "" {
if strings.HasPrefix(geminiKey, "sk-") {
if strings.HasPrefix(geminiKey, "sk-ant-") {
log.Fatal("GEMINI_API_KEY appears to be an Anthropic key (starts with 'sk-ant-'), please use anthropic-api-key input instead")
}
log.Fatal("GEMINI_API_KEY appears to be an OpenAI key (starts with 'sk-'), please use openai-api-key input instead")
}
if !strings.HasPrefix(geminiKey, "AIza") {
log.Fatal("GEMINI_API_KEY appears to be invalid format (should start with 'AIza')")
}
}
}

func getDefaultModel(openaiKey, anthropicKey, geminiKey string) string {
if openaiKey != "" {
return "gpt-4o"
}
if anthropicKey != "" {
return "claude-3-5-sonnet"
}
if geminiKey != "" {
return "gemini-2.0-flash"
}

return ""
}

func validateConfig(config batchConfig) {
if config.repository == "" {
log.Fatal("Missing required environment variable: REPOSITORY")
}

validateProviderTokens(config)
validateModeRequirements(config)
}

func validateProviderTokens(config batchConfig) {
if config.provider == "gitlab" {
if config.gitlabToken == "" {
log.Fatal("Missing GITLAB_TOKEN for gitlab provider")
Expand All @@ -111,13 +168,15 @@ func validateConfig(config batchConfig) {
log.Fatal("Missing GITHUB_TOKEN for github provider")
}
}
}

func validateModeRequirements(config batchConfig) {
if !config.useGitClone && config.sourceBase64 == "" {
log.Fatal("Missing SOURCE_BASE64 for legacy mode")
}

if config.useGitClone && config.llmAPIKey == "" {
log.Fatal("Missing LLM_API_KEY for git clone mode")
if config.useGitClone && config.openaiKey == "" && config.anthropicKey == "" && config.geminiKey == "" {
log.Fatal("Missing API key for git clone mode (need OPENAI_API_KEY, ANTHROPIC_API_KEY, or GEMINI_API_KEY)")
}
}

Expand Down Expand Up @@ -239,34 +298,6 @@ func decodeSourceData(dag *dagger.Client, sourceBase64 string) *dagger.Directory
return dag.Directory().WithNewFile("workflows.tar.gz", string(sourceData))
}

func setupLLMEnvironment(llmAPIKey string) {
// Detect provider based on key format and set only the appropriate env var
var providerKey string
var providerName string

if strings.HasPrefix(llmAPIKey, "sk-") {
providerKey = "OPENAI_API_KEY"
providerName = "OpenAI"
} else if strings.HasPrefix(llmAPIKey, "sk-ant-") {
providerKey = "ANTHROPIC_API_KEY"
providerName = "Anthropic"
} else if strings.HasPrefix(llmAPIKey, "AIza") {
providerKey = "GEMINI_API_KEY"
providerName = "Gemini"
} else {
// Default to OpenAI if format is unknown
providerKey = "OPENAI_API_KEY"
providerName = "OpenAI (default)"
log.Printf("Warning: Unknown API key format, defaulting to OpenAI")
}

if err := os.Setenv(providerKey, llmAPIKey); err != nil {
log.Printf("Warning: Failed to set %s: %v", providerKey, err)
} else {
log.Printf("Set LLM environment for %s", providerName)
}
}

func incrementUsage(repository string, success bool) error {
serviceURL := os.Getenv("SERVICE_URL")
if serviceURL == "" {
Expand Down
13 changes: 5 additions & 8 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func TestScanAndFixWorflowsImpl(t *testing.T) {
// Step 1: Run ZIZMOR auto-fix
mockZizmor.EXPECT().
RunZizmorAutoFix(gomock.Any(), mockDirectory).
Return(mockDirectory, "Fixed 2 security issues automatically", nil)
Return(mockDirectory, []zizmor.Finding{}, "Fixed 2 security issues automatically", nil)

// Step 2: Check remaining issues - none found
mockZizmor.EXPECT().
Expand Down Expand Up @@ -84,7 +84,7 @@ func TestScanAndFixWorflowsImpl(t *testing.T) {
// Step 1: Run ZIZMOR auto-fix
mockZizmor.EXPECT().
RunZizmorAutoFix(gomock.Any(), mockDirectory).
Return(mockDirectory, "Fixed some issues", nil)
Return(mockDirectory, []zizmor.Finding{}, "Fixed some issues", nil)

// Step 2: Check remaining issues - some found
remainingIssues := `[{"desc": "manual fix needed"}]`
Expand Down Expand Up @@ -134,10 +134,7 @@ func TestScanAndFixWorflowsImpl(t *testing.T) {
// Step 1: ZIZMOR auto-fix fails
mockZizmor.EXPECT().
RunZizmorAutoFix(gomock.Any(), mockDirectory).
Return(nil, "", errors.New("ZIZMOR container failed"))

// No other calls should happen after failure

Return(nil, []zizmor.Finding{}, "", errors.New("ZIZMOR container failed"))
return mockZizmor, mockAgent, mockGithub, mockDirectory
},
expectedResult: "",
Expand All @@ -156,7 +153,7 @@ func TestScanAndFixWorflowsImpl(t *testing.T) {
// Step 1: Run ZIZMOR auto-fix
mockZizmor.EXPECT().
RunZizmorAutoFix(gomock.Any(), mockDirectory).
Return(mockDirectory, "Fixed some issues", nil)
Return(mockDirectory, []zizmor.Finding{}, "Fixed some issues", nil)

// Step 2: Check remaining issues - some found
remainingIssues := `[{"desc": "complex issue"}]`
Expand Down Expand Up @@ -193,7 +190,7 @@ func TestScanAndFixWorflowsImpl(t *testing.T) {
// Step 1: Run ZIZMOR auto-fix
mockZizmor.EXPECT().
RunZizmorAutoFix(gomock.Any(), mockDirectory).
Return(mockDirectory, "Fixed issues", nil)
Return(mockDirectory, []zizmor.Finding{}, "Fixed issues", nil)

// Step 2: Check remaining issues - none
mockZizmor.EXPECT().
Expand Down
14 changes: 14 additions & 0 deletions mocks/client_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 38 additions & 0 deletions mocks/container_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 42 additions & 0 deletions mocks/env_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions mocks/llm_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading