Commit 87fe0d2
Codebase audit: SecurityScannerProvider and scanner plugin (#279)
* docs: actor model integration design document
Explores integrating goakt v4 actor framework into the workflow engine
as a complementary paradigm alongside pipelines. Covers architecture,
YAML config schemas, deployment model, documentation strategy, and the
path toward a future actor-native engine.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: actor model implementation plan
11 tasks, 29 tests, TDD throughout. Covers plugin skeleton, actor.system
module, actor.pool module, bridge actor, step.actor_send/ask, actor
workflow handler, schemas, config example, and integration tests.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: add goakt v4 dependency and fix k8s API compat
Add github.com/tochemey/goakt/v4 as a dependency. Fix
VolumeResourceRequirements type change from k8s API v0.35 upgrade.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(actors): plugin skeleton, actor.system and actor.pool modules with tests
* feat(actors): bridge actor and message types for pipeline-in-actor execution
* feat(actors): bridge actor that executes step pipelines inside goakt
BridgeActor is the core goakt<->pipeline integration:
- Implements goakt v4 Actor interface (PreStart/Receive/PostStop)
- Dispatches incoming ActorMessages to HandlerPipeline by message type
- Builds PipelineContext with .message, .state, .actor template variables
- Falls back to inline step.set creation when no step registry is available
- Merges last step output back into actor state for persistence
- Returns error map for unknown message types (not panics)
- 3 tests pass: receive, unknown type, state persistence across messages
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(actors): integration tests, step.actor_send/ask, and actor spawning
Integration tests (Task 10):
- TestIntegration_FullActorLifecycle: full lifecycle from module creation
through message passing and state persistence verification
- TestIntegration_MultipleActorsIndependentState: two actors maintain
independent state — no shared-state bugs
step.actor_send (Task 5): fire-and-forget Tell to actor pools,
template resolution for identity/payload, pool lookup from metadata
step.actor_ask (Task 6): request-response Ask with configurable timeout,
template resolution, returns actor response as step output
module_pool.go: GetOrSpawnActor helper for identity-based spawning,
pids map with mutex for concurrent access, SetStepRegistry injection,
handlers typed to map[string]*HandlerPipeline
bridge_actor.go: NewBridgeActor constructor, State() inspector method
All 25 tests pass with -race flag.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(actors): step.actor_send and step.actor_ask pipeline steps
- step.actor_send: fire-and-forget Tell to identity-based or pool actors
- step.actor_ask: request-response Ask with configurable timeout (default 10s)
- Both resolve message/identity as template expressions via PipelineContext
- Use GetOrSpawnActor for auto-managed pools, ActorOf for permanent pools
- Registered in plugin.StepFactories via wrapStepFactory helper
- 9 unit tests covering config validation and timeout parsing
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(actors): actor workflow handler, wiring hook, and pool actor management
* feat(actors): module and step schemas, handler tests
* docs(actors): example config demonstrating actor-based order processing
Shows actor.system, actor.pool, and step.actor_ask in a complete
HTTP + actor workflow: stateful order processing where each order_id
maps to its own auto-managed actor instance.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: add actor model types to DOCUMENTATION.md
- actor.system and actor.pool module types in Actor Model section
- step.actor_send and step.actor_ask in Pipeline Steps table
- Actors workflow type in Workflow Types section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs(actors): refine config example with cleaner format and cancel workflow
* fix(actors): address spec review — pool lookup via service registry, permanent pool spawning
- Fix step.actor_send and step.actor_ask to look up pools via
app.GetService("actor-pool:<name>") instead of unreachable
pc.Metadata["__actor_pools"] map
- Implement permanent pool actor spawning in ActorPoolModule.Start()
— spawns poolSize BridgeActor instances into the goakt system
- Remove double error response in BridgeActor.Receive() — use only
ctx.Err(err), not both ctx.Err and ctx.Response
- Remove unused ActorResponse dead code from messages.go
- Add BridgeGrain integration test (state persistence via grain API)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(actors): nil guard before system access, safe type assertions, ask step test
- Move nil check for pool.system before ActorSystem() call in both
step.actor_send and step.actor_ask to prevent nil pointer dereference
- Use sys variable consistently instead of redundant ActorSystem() calls
- Convert bare type assertions to two-value form in integration and
bridge actor tests to fail gracefully instead of panicking
- Add TestActorAskStep_RequiresMessageType test for factory validation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(actors): address Copilot review — naming, CBOR tags, identity validation, step caching
- Spawn primary actor under pool name so sys.ActorOf(ctx, poolName)
succeeds for permanent pools (was spawning pool-0..N only)
- Add CBOR struct tags to ActorMessage for cluster mode serialization
- Validate identity is required for auto-managed pools at Execute time
instead of silently falling through to a failing ActorOf path
- Cache step instances in executePipeline to avoid rebuilding per message
- Remove AI-generated plan doc (local paths, Claude instructions)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(actors): implement runtime routing, recovery, and remove cluster-only fields
Steps now use pool.SelectActor(msg) for permanent pools instead of
sys.ActorOf(), enabling actual routing strategy execution. Round-robin,
random, broadcast, and sticky routing all work at runtime. Recovery
supervisors are applied via actor.WithSupervisor() during Spawn.
Removed non-functional cluster-only schema fields (cluster, metrics,
tracing, placement, targetRoles, failover) since single-node is the
current scope. Added 10 new tests covering routing distribution,
permanent pool spawning, broadcast delivery, and recovery. All 41
actor plugin tests pass. Example config updated with permanent pool.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix(actors): resolve lint issues and step caching race condition
- Pre-build step instances during pool init (preBuildSteps) instead of
caching in shared handler maps at runtime, eliminating a data race
when multiple actors process messages concurrently
- Convert if-else chain to switch statement (gocritic)
- Guard negative int-to-uint32 conversion (gosec G115)
- Remove unnecessary nil check around range (staticcheck S1031)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: tidy example module for goakt v4 transitive deps
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address review comments — fresh steps per execution, RequiresServices, error handling
- Build fresh step instances per execution in executePipeline() to avoid
sharing mutable state across concurrent actors in the same pool
- Remove BuiltSteps field from HandlerPipeline and preBuildSteps() from pool
(no longer needed with per-execution step building)
- Return real error for unknown message types instead of map with error key
- Accumulate all step outputs into actor state, not just the last step's
- Add RequiresServices() on ActorPoolModule to declare dependency on its
actor.system module for correct init ordering
- Document nil return convention in module factories (engine handles nil)
- Update TestBridgeActor_UnknownMessageType for new error behavior
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add codebase audit design and implementation plan
Comprehensive audit found 3 stubbed scan steps in core engine, confirmed
all external plugins are fully implemented, and identified 8 plugins with
zero scenario coverage.
Plan: SecurityScannerProvider interface, DockerSandbox module, security
scanner plugin, 4 public scenarios, 5 private scenarios.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(module): add SecurityScannerProvider interface and rewrite scan steps
- Create scan_provider.go with SecurityScannerProvider interface and
SASTScanOpts/ContainerScanOpts/DepsScanOpts config structs; export
SeverityRank as a public wrapper around the existing severityRank helper
- Rewrite ScanSASTStep, ScanContainerStep, and ScanDepsStep Execute()
methods to delegate to a SecurityScannerProvider looked up from the
modular service registry under "security-scanner"
- Steps return a clear error when no provider is configured instead of
ErrNotImplemented; severity gate evaluation uses existing EvaluateGate()
- Add scan_provider_test.go with mock provider covering success, gate
failure, and provider error cases for all three scan steps
- Update pipeline_step_scan_test.go to verify the no-provider error path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(scanner): add security.scanner built-in plugin with mock mode
Adds plugins/scanner package implementing SecurityScannerProvider.
The security.scanner module registers as "security-scanner" service,
enabling the existing scan_sast/scan_container/scan_deps steps.
Supports mock mode with configurable findings for testing, and
defaults to sensible scanner backends (semgrep, trivy, grype).
12 tests covering all scan types, gate evaluation, and config.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(plugin/external): add SecurityScannerRemoteModule adapter for security.scanner
- Add security_scanner_adapter.go with SecurityScannerRemoteModule that wraps
RemoteModule and registers a remoteSecurityScannerProvider in the service
registry on Init(app), enabling core scan steps to find the provider via
app.GetService("security-scanner", &provider)
- Update adapter.go ModuleFactories() to wrap security.scanner remote modules
with SecurityScannerRemoteModule instead of the plain RemoteModule
- remoteSecurityScannerProvider implements module.SecurityScannerProvider by
delegating ScanSAST/ScanContainer/ScanDeps to InvokeService gRPC calls
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: address PR #279 review comments
- Add nil guards for app in all 3 scan steps
- Validate severity threshold in scan step factories
- Default fail_on_severity to "high" (was "error", not a valid severity)
- Validate scanner module mode config
- Log errors in scanner module factory
- Update plugin description to not claim CLI mode support
- Add TODO for context propagation in remote scanner adapter
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address remaining PR #279 review comments
- Validate target_image is non-empty in scan_container step factory
- Validate mockFindings scan type keys (sast/container/deps only)
- Error on malformed finding items instead of silently skipping
- Handle both int and float64 types for line field in parseMockFindings
- Remove tool-specific instructions and absolute paths from plan doc
- Fix provider lookup reference to match implementation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>1 parent e59d21d commit 87fe0d2
14 files changed
Lines changed: 1949 additions & 74 deletions
File tree
- docs/plans
- module
- plugins
- all
- scanner
- plugin/external
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
0 commit comments