Skip to content
3 changes: 1 addition & 2 deletions backend/secuscan/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -839,5 +839,4 @@ async def get_db() -> Database:
"""Get the global database instance."""
if db is None:
raise RuntimeError("Database not initialized")

return db
return db
1 change: 0 additions & 1 deletion backend/secuscan/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ async def redirect_api_openapi():
app.include_router(router)
app.include_router(saved_views_router)


# Health check endpoint
@app.get("/api/v1/health")
async def health_check():
Expand Down
43 changes: 26 additions & 17 deletions backend/secuscan/workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@
from .database import get_db
from .config import settings
from .executor import executor

from .execution_context import normalize_execution_context
from .platform_resources import get_target_policy

logger = logging.getLogger(__name__)
class WorkflowScheduler:
def __init__(self):
Expand Down Expand Up @@ -71,37 +73,44 @@ def _should_run(self, now: datetime, last_run_at: str | None, schedule_seconds:
elapsed = (now - last).total_seconds()
return elapsed >= schedule_seconds
async def _run_workflow(self, workflow_id: str, steps: List[Dict[str, Any]]):
db = await get_db()
logger.info("Running workflow %s with %d step(s)", workflow_id, len(steps))
for step in steps:
plugin_id = step.get("plugin_id")
inputs = step.get("inputs") or {}
if not plugin_id:
continue
request_id = get_request_id()
execution_context = normalize_execution_context(step.get("execution_context") or {})
target_policy = await get_target_policy(db, "default", execution_context.get("target_policy_id"))
execution_context = normalize_execution_context(
step.get("execution_context") or {}
)
target_policy = await get_target_policy(
db,
"default",
execution_context.get("target_policy_id")
)
safe_mode = bool(
settings.safe_mode_default
and not (target_policy and target_policy.get("allow_public_targets"))
)
and not (
target_policy
and target_policy.get("allow_public_targets")
)
)
effective_inputs = dict(inputs)
effective_inputs.pop("safe_mode", None)
effective_inputs["safe_mode"] = safe_mode

task_id = await executor.create_task(
plugin_id,
effective_inputs,
safe_mode=safe_mode,
preset=step.get("preset"),
execution_context=execution_context,
consent_granted=True,
)

async def run_task(task_id: str) -> None:
plugin_id,
effective_inputs,
safe_mode=safe_mode,
preset=step.get("preset"),
execution_context=execution_context,
consent_granted=True,
)
async def run_task():
set_request_id(request_id)
await executor.execute_task(task_id)
asyncio.create_task(run_task())

asyncio.create_task(run_task(task_id))


scheduler = WorkflowScheduler()
scheduler = WorkflowScheduler()
5 changes: 5 additions & 0 deletions frontend/testing/unit/pages/ToolConfigDynamic.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,11 @@ describe('ToolConfig dynamic schema flow', () => {
}),
true,
'quick',
expect.objectContaining({
evidence_level: 'standard',
scan_profile: 'standard',
validation_mode: 'proof',
}),
)
})
})
Expand Down
2 changes: 1 addition & 1 deletion frontend/testing/unit/pages/ToolConfigTimeout.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,6 @@ describe('ToolConfig timeout control', () => {
// min from field.validation
expect(input).toHaveAttribute('min', '30')
// max is min(field.validation.max, server default_timeout)
expect(input).toHaveAttribute('max', '600')
expect(input).toHaveAttribute('max', '7200')
})
})
26 changes: 23 additions & 3 deletions frontend/testing/unit/pages/Workflows.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,17 @@ const mockWorkflow = {
name: 'Nightly Scan',
schedule_seconds: 3600,
enabled: true,
steps: [{ plugin_id: 'nmap', inputs: {} }],
steps: [
{
plugin_id: 'nmap',
inputs: {},
execution_context: {
evidence_level: 'standard'as const,
scan_profile: 'standard',
validation_mode: 'proof' as const,
},
},
],
last_run_at: null,
queued_task_ids: [],
}
Expand Down Expand Up @@ -130,8 +140,18 @@ describe('Workflows — create action', () => {
name: 'Nightly Scan',
schedule_seconds: 7200,
enabled: true,
steps: [{ plugin_id: '', inputs: {} }],
})
steps: [
{
plugin_id: '',
inputs: {},
execution_context: {
evidence_level: 'standard',
scan_profile: 'standard',
validation_mode: 'proof' as const,
},
},
],
})
})
})

Expand Down
Loading