feat: mandatory engine lifecycle + text-file path guardrails#7
Conversation
|
ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing |
There was a problem hiding this comment.
Pull request overview
This PR introduces an explicit lifecycle contract for AutomationEngine (requiring start() before triggering runs, and adding stop() teardown semantics) and adds a new TextFileGuardrail component to validate string file-path payloads against traversal/absolute-path/null-byte/extension rules. It also updates package exports and expands regression tests to cover the new lifecycle and guardrail behaviors.
Changes:
- Added mandatory engine lifecycle (
start()/stop(),is_running, andEngineNotStartedErrorenforcement intrigger()/trigger_type()), withdefault_engineauto-started for backward compatibility. - Added
TextFileGuardrail+DEFAULT_TEXT_EXTENSIONSguardrail component for file-path payload validation. - Added/updated tests and public exports for the new engine error and guardrail symbols.
Reviewed changes
Copilot reviewed 4 out of 8 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| tests/test_automation_review_regressions.py | Adds regression coverage for lifecycle enforcement and TextFileGuardrail validation + pipeline behavior. |
| src/automation/guardrails.py | Introduces TextFileGuardrail and DEFAULT_TEXT_EXTENSIONS for safe text-file path payload validation. |
| src/automation/engine.py | Adds lifecycle management (start/stop), enforces “must be started” on triggers, and auto-starts default_engine. |
| src/automation/init.py | Exposes EngineNotStartedError, TextFileGuardrail, and DEFAULT_TEXT_EXTENSIONS via package exports. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| class EngineNotStartedError(RuntimeError): | ||
| """Raised when :meth:`AutomationEngine.trigger` is called before :meth:`start`.""" |
| Stop the engine and tear down all registered components. | ||
|
|
||
| After :meth:`stop` returns, :meth:`trigger` / :meth:`trigger_type` | ||
| will raise :class:`EngineNotStartedError` until :meth:`start` is | ||
| called again. All component teardown hooks are invoked; any |
| if not self._running: | ||
| self._running = True | ||
| logger.info("AutomationEngine started") | ||
| return self |
The engine had no lifecycle contract — it would run automations in any state, with no enforcement boundary. Text-file path payloads had no safety validation, leaving pipelines open to path traversal and extension-based attacks.
Engine lifecycle (
engine.py)start()/stop()methods toAutomationEngine;stop()triggersteardown()on all registered componentstrigger()/trigger_type()now raiseEngineNotStartedErrorif the engine hasn't been started — lifecycle is mandatoryis_runningproperty exposes current statedefault_engineis auto-started to preserve backward compatibilityText-file guardrail (
guardrails.py, new)TextFileGuardrail— pipeline component that validates string file-path payloads before downstream steps execute..), null bytes, absolute paths (configurable), disallowed extensionsDEFAULT_TEXT_EXTENSIONS— configurable frozenset of allowed extensions (.txt,.log,.csv,.md,.rst, …)Exports
EngineNotStartedError,TextFileGuardrail, andDEFAULT_TEXT_EXTENSIONSadded toautomation.__all__.Usage