Skip to content

fix(email,api): AUTH PLAIN fallback for SMTP + expose input/error in workflow runs list#6293

Merged
houko merged 3 commits into
librefang:mainfrom
DaBlitzStein:fix/email-auth-plain-fallback
Jun 24, 2026
Merged

fix(email,api): AUTH PLAIN fallback for SMTP + expose input/error in workflow runs list#6293
houko merged 3 commits into
librefang:mainfrom
DaBlitzStein:fix/email-auth-plain-fallback

Conversation

@DaBlitzStein

Copy link
Copy Markdown
Contributor

Two small fixes:

1. Email adapter: AUTH PLAIN fallback

Python's smtplib.login() uses AUTH LOGIN but some providers
(e.g. Mailgun) reject LOGIN and only accept PLAIN. Add a
fallback: try LOGIN first, retry with AUTH PLAIN on auth failure.
Non-breaking — standard servers use the fast path.

2. Workflow runs list: expose input and error

GET /api/workflows/{id}/runs now returns "input" (parameters used)
and "error" (failure reason) in addition to existing fields.
Gives the dashboard enough data to show what params were used and
why a run failed without requiring a second request to the detail
endpoint.

Verification:

  • Email fallback tested against Mailgun SMTP
  • Workflow runs list confirmed working on rodela

…workflow runs list

email.py: smtp.login() uses AUTH LOGIN which some providers
(e.g. Mailgun) reject. Try LOGIN first; fall back to AUTH PLAIN
on SMTPAuthenticationError so servers that only advertise PLAIN
still work.

workflow.rs: add input (the parameters used) and error to
GET /api/workflows/{id}/runs so the dashboard shows what
parameters were used and why a run failed.
@github-actions github-actions Bot added size/S 10-49 lines changed area/sdk JavaScript and Python SDKs labels Jun 23, 2026
…estart

update_workflow() only updated the in-memory HashMap but never
wrote the JSON file. Agents assigned to steps via the dashboard
would disappear on daemon restart because the old file still
had the previous state. Now mirrors register()'s atomic write.
@github-actions github-actions Bot added the area/kernel Core kernel (scheduling, RBAC, workflows) label Jun 23, 2026

@houko houko left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two issues found.

1. Missing integration test for list_workflow_runs response shape change
(crates/librefang-api/src/routes/workflows/workflow.rs)

The list_workflow_runs handler now includes "input" and "error" in the JSON response object. Per CLAUDE.md (§ "MANDATORY: Integration Testing"), route shape changes require a #[tokio::test] against TestServer in the matching tests/*.rs file. The test should spawn the router, fire GET /api/workflows/{id}/runs, and assert both fields appear in the response (and that "error" is populated on a failed run). Without it the serialization path and field names are not regression-tested.

2. Security-rationale comment removed from the STARTTLS check
(sdk/python/librefang/sidecar/adapters/email.py, STARTTLS branch)

The deleted block explained why STARTTLS is required (credential leakage over plaintext TCP, parity with lettre's starttls_relay semantics in the Rust adapter). The raise RuntimeError message says "refusing to send" but doesn't convey the threat model or the cross-language parity constraint. Per CLAUDE.md the comment qualifies as "hidden constraint / behaviour that would surprise a reader" — the STARTTLS requirement is non-obvious to someone adding a new SMTP provider. Please restore it (or an equivalent sentence) on the raise RuntimeError line.


Generated by Claude Code

@houko

houko commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Reviewed the diff. The email AUTH PLAIN fallback looks correct — SASL PLAIN \0user\0pass, the 235 success check, and it runs inside the TLS channel (after starttls() / over SMTP_SSL) so credentials are never sent in plaintext. Two things on the API/kernel side, plus a note on the description.

1. (should fix) GET /api/workflows/{id}/runs ignores {id} — and the new input field turns that into cross-workflow data exposure

list_workflow_runs binds the path param as Path(_id) (unused) and calls list_runs(None)crates/librefang-api/src/routes/workflows/workflow.rs:1419-1421. WorkflowEngine::list_runs(None) only filters by state, never by workflow_id (crates/librefang-kernel/src/workflow.rs:2261), so the endpoint returns runs for every workflow, not the one named in the path.

This is pre-existing, but this PR adds "input": r.input to the payload, and WorkflowRun.input is the run's full initial-input string. So GET /api/workflows/A/runs now also returns the complete input of every run of workflows B, C, … — both a correctness bug (the list is wrong) and a cross-workflow input leak. Since the PR already touches this handler, please scope it by id here (keep only r.workflow_id == id, or add a workflow-id filter to list_runs).

2. (nice to fix) update_workflow does disk I/O while holding the workflows write lock

The new persistence block runs create_dir_all + write + rename inside the Entry::Occupied arm while still holding self.workflows.write().awaitcrates/librefang-kernel/src/workflow.rs:1991-1998. register (:1837-1860) and remove_workflow (:2003-2012) both deliberately do their disk I/O outside the lock. As written, every update_workflow blocks all concurrent workflow reads/writes for the duration of the write + rename. Suggest: entry.insert(workflow.clone()) under the lock, drop the guard, then persist.

3. Description / tests

The body says "Two small fixes", but the diff has three changes — the update_workflow disk-persistence change in crates/librefang-kernel/src/workflow.rs isn't mentioned, and it's the riskiest of the three. It also ships without a test (compare register's register_writes_atomically_and_cleans_tmp) and isn't covered by the Verification section. Worth adding it to the description and a persistence unit test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/kernel Core kernel (scheduling, RBAC, workflows) area/sdk JavaScript and Python SDKs size/S 10-49 lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants