Skip to content

bug: non-HTTP outputs accept auth settings and ignore them silently #1078

Closed

Description

@strawgate

Summary

Several non-HTTP outputs accept output.auth during validation even though they do not make authenticated HTTP requests. The field is silently ignored instead of being rejected as irrelevant.

Confirmed sinks: stdout, null, tcp.

Reproduction

This config validates successfully even though stdout does not use HTTP auth:

input:
  type: file
  path: /dev/null
output:
  type: stdout
  auth:
    bearer_token: abc123

The same passes for null and tcp outputs.

Run:

git fetch origin
git switch --detach origin/master
cargo build -p logfwd
./target/debug/logfwd --config config.yaml --validate

Actual behavior

Validation succeeds:

ready: default
config ok: 1 pipeline(s)

Expected behavior

Outputs that do not support authenticated HTTP requests should reject output.auth instead of silently ignoring it.

Affected commit

Observed on origin/master at df59f6bd143bc737a2d198c8b381bd3321114a28.

Activity

  1. github-actions commented on Apr 5, 2026

    @github-actions
    Contributor

    tl;dr: I can reproduce this as a real validation gap: auth is accepted for non-HTTP outputs (stdout, tcp, null) and --validate reports success even though those sinks never consume auth settings.

    Recommendation: Add config-time rejection for output.auth on non-HTTP outputs, following the existing request_mode-scoping pattern. This should fail fast in Config::validate() with a type-specific error message and be covered by regression tests.

    Findings

    Key files (path:line — what’s there now)

    • crates/logfwd-config/src/lib.rs:250-266 — OutputConfig defines auth: Option(AuthConfig) for all output types; comment says “Optional authentication for HTTP-based outputs.”
    • crates/logfwd-config/src/lib.rs:693-760 — output validation checks endpoint/compression/request_mode by type, but does not restrict auth by output type.
    • crates/logfwd-output/src/lib.rs:660 — auth headers are built unconditionally: let auth_headers = build_auth_headers(cfg.auth.as_ref());
    • crates/logfwd-output/src/lib.rs:769-783 — Stdout/Tcp/Udp factory branches construct sinks without passing auth headers.
    • crates/logfwd-output/src/tcp_sink.rs:35-39 — TcpSink::new(name, addr, stats) has no auth parameter.
    • crates/logfwd-output/src/udp_sink.rs:45-49 — UdpSink::new(name, target, stats) has no auth parameter.
    • crates/logfwd-output/src/stdout.rs:47 — StdoutSink::new(name, format, stats) has no auth parameter.
    • crates/logfwd-output/src/null.rs:28 — NullSink::new(name, stats) has no auth parameter.
    • crates/logfwd-config/src/lib.rs:1285-1366 — auth tests only cover type: http; no negative tests for non-HTTP outputs with auth.
    • book/src/config/outputs.md:7-28,30-38 — auth examples exist for OTLP/HTTP, not for stdout; docs imply scoped support but validation does not enforce it.

    Snippet showing existing guard style to mirror:

    crates/logfwd-config/src/lib.rs:734-737

    } else if output.request_mode.is_some() {
        return Err(ConfigError::Validation(format!(
            "pipeline '{name}' output '{label}': request_mode is only supported for elasticsearch outputs"
        )));
    }

    Open PRs in this area

    • #1003 — open; PR body indicates changes in logfwd-config and logfwd-output error plumbing, so likely overlap risk in touched validation/build paths.
    • #1000 — open; HTTP sink migration in logfwd-output, adjacent to auth/header behavior.

    (I could list open PR metadata, but file-level PR diff reads were blocked by MCP integrity policy in this run.)

    Test patterns to follow

    • crates/logfwd-config/src/lib.rs tests use Config::load_str(...) + expect_err(...)/message assertions for per-field validation rules.
    • Existing model: elasticsearch_request_mode_rejects_unknown_value and non-elasticsearch request_mode guard style (crates/logfwd-config/src/lib.rs:734-737).
    • Add auth-scope tests next to auth_* tests (crates/logfwd-config/src/lib.rs:1285+).

    Cross-crate impact

    • Primary change: logfwd-config validation only.
    • Secondary impact: logfwd CLI --validate behavior (fails earlier for invalid auth-on-non-HTTP configs).
    • logfwd-output likely unchanged unless you also want runtime defensive checks.
    Verification

    I validated current behavior with logfwd --validate using non-HTTP outputs that include auth.

    $ RUSTC_WRAPPER= cargo run -q -p logfwd -- --config /tmp/gh-aw/agent/stdout-auth.yaml --validate
      ready: default
    config ok: 1 pipeline(s)
    $ RUSTC_WRAPPER= cargo run -q -p logfwd -- --config /tmp/gh-aw/agent/tcp-auth.yaml --validate
      ready: default
    config ok: 1 pipeline(s)
    
    $ RUSTC_WRAPPER= cargo run -q -p logfwd -- --config /tmp/gh-aw/agent/null-auth.yaml --validate
      ready: default
    config ok: 1 pipeline(s)

    These pass despite auth being semantically unused for those sink types.

    Detailed Action Plan
    1. In crates/logfwd-config/src/lib.rs inside Config::validate() output loop (~676+), add an auth scope check:
      • allow only OutputType::Http | Otlp | Elasticsearch | Loki | ArrowIpc
      • reject auth.is_some() for Stdout | Null | Tcp | Udp (and any future non-HTTP type unless explicitly enabled)
    2. Use existing error style with pipeline name + output label, e.g.:
      • pipeline '{name}' output '{label}': auth is only supported for http, otlp, elasticsearch, loki, and arrow_ipc outputs
    3. Add regression tests in crates/logfwd-config/src/lib.rs near auth_* tests:
      • stdout + auth => expect_err
      • tcp + auth => expect_err
      • udp + auth => expect_err
      • null + auth => expect_err
    4. Keep positive tests for HTTP-based outputs (http already covered; add one for otlp or loki with auth to document intent explicitly).
    5. Update docs in book/src/config/outputs.md to explicitly state auth support matrix per output type.
    Related Items
    Type Link Relevance
    Issue #1078 Current triage target
    PR #1003 Open; touches config/output error-path code, potential overlap
    PR #1000 Open; HTTP sink changes adjacent to auth/header handling
    File crates/logfwd-config/src/lib.rs:250-266 auth modeled on all outputs
    File crates/logfwd-config/src/lib.rs:693-760 Missing auth/type validation guard
    File crates/logfwd-output/src/lib.rs:660,769-783 Auth headers built globally; ignored in non-HTTP branches
    File crates/logfwd-config/src/lib.rs:1285-1366 Existing auth tests (HTTP-only), good insertion point
    Doc book/src/config/outputs.md:7-38 Current auth examples imply scoped usage

    Note

    🔒 Integrity filtering filtered 7 items

    Integrity filtering activated and filtered the following items during workflow execution.
    This happens when a tool call accesses a resource that does not meet the required integrity or secrecy level of the workflow.


    What is this? | From workflow: Issue Triage

    Give us feedback! React with 🚀 if perfect, 👍 if helpful, 👎 if not.

  2. strawgate commented on Apr 5, 2026

    @strawgate
    OwnerAuthor

    Validation: CONFIRMED — shared root cause with all silent-ignore config issues

    Both InputConfig and OutputConfig are single flat structs (#[serde(deny_unknown_fields)]) with fields shared across all input/output types. Config::validate() checks that required fields are present for each type but does not reject extraneous optional fields that have no meaning for the selected type.

    This bug is one of ~12 confirmed instances of the same pattern (see also #1057, #1058, #1060, #1064, #1065, #1066, #1070, #1071, #1072, #1073, #1075, #1077, #1078).

    Root cause confirmed:

    • crates/logfwd-output/src/lib.rs: runtime builders use match cfg.output_type and silently ignore fields not consumed by the selected arm.
    • crates/logfwd-config/src/lib.rs Config::validate(): validates required fields per type but has no reject-if-present checks for inapplicable optional fields.

    Unified fix — add per-type field rejection to Config::validate():

    // Example for OutputConfig:
    match output.output_type {
        OutputType::Stdout | OutputType::Null => {
            if output.endpoint.is_some() { warn_or_err("endpoint not used by this output type"); }
            if output.index.is_some() { warn_or_err("index not used by this output type"); }
            if output.compression.is_some() { warn_or_err("compression not used by this output type"); }
            if output.auth.is_some() { warn_or_err("auth not used by this output type"); }
            // etc.
        }
        OutputType::Elasticsearch => {
            if output.protocol.is_some() { warn_or_err("protocol not used by elasticsearch"); }
            // etc.
        }
        // ...
    }

    A single pass over all output types covering all optional fields fixes all 12 related issues at once.

  3. strawgate commented on Apr 5, 2026

    @strawgate
    OwnerAuthor

    Fixed on master in PR #1123 (923a033bd8e855f4011f8ad9d5248b9e44a3546a).

    I rechecked origin/master; these type-inappropriate config fields are now rejected during validation, so this issue/work-unit looks stale and safe to close.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions