Skip to content

feat(http_request): add env-backed credential profiles and onboarding policy presets#2148

Merged
theonlyhennygod merged 1 commit intomainfrom
issue-reddit-hardening-onboard-cred-flow
Feb 28, 2026
Merged

feat(http_request): add env-backed credential profiles and onboarding policy presets#2148
theonlyhennygod merged 1 commit intomainfrom
issue-reddit-hardening-onboard-cred-flow

Conversation

@theonlyhennygod
Copy link
Copy Markdown
Collaborator

@theonlyhennygod theonlyhennygod commented Feb 28, 2026

Summary

Problem

http_request calls currently require callers to pass raw auth headers/tokens directly in tool arguments, which increases secret-handling risk and onboarding friction.

Why It Matters

Users need a safer default for authenticated HTTP automation and a guided onboarding path for common integrations.

What Changed

  • Added env-backed http_request.credential_profiles config (header name + env var + optional value prefix).
  • Added config validation for profile names, env var names, and header names.
  • Updated http_request runtime to:
    • inject credential headers from profile + environment values,
    • detect explicit header conflicts,
    • redact injected secret values in output.
  • Added onboarding wizard presets for http_request policy modes and optional credential-profile setup.
  • Updated docs in docs/config-reference.md.

Label Snapshot

  • config: core
  • docs
  • onboard: wizard
  • risk: high
  • size: L
  • tool: http_request

Change Metadata

Linked Issue

Validation Evidence

Commands executed locally in this pass:

  • cargo test credential_profile -- --nocapture
  • cargo fmt --all -- --check

Current outcome:

  • Build/test currently fail in baseline code paths unrelated to this PR scope (channel/tool symbols missing in current repo state), and CI runner health has also shown infra issues (disk space/install failures) on prior runs.
  • Rebase has been pushed to refresh CI against latest main.

Security Impact

  • Reduces accidental plaintext credential handling by shifting auth values to environment variables.
  • Adds header-conflict checks to prevent ambiguous or duplicated auth headers.
  • Adds explicit secret redaction in http_request output paths.

Privacy and Data Hygiene

  • No personal data added.
  • No secrets hardcoded.
  • Docs and examples use placeholder env vars/values only.

Compatibility

  • Backward compatible for existing http_request usage without credential_profile.
  • New profile configuration is opt-in.

i18n Follow-Through

  • No new non-English UI/runtime strings introduced.

Human Verification

  • Reviewed PR diff and conflict resolution after rebase.
  • Verified scoped files remained within feature intent.

Side Effects

  • Expanded onboarding flow for users configuring web tools.
  • Additional config validation may reject previously accepted malformed profile entries.

Rollback Plan

  1. Revert PR #2148 from main.
  2. Remove credential_profiles usage from runtime and config.
  3. Restore prior onboarding branch behavior.

Risks and Mitigations

  • Risk: misconfigured env/profile names causing request failures.
  • Mitigation: validation at config-load time + explicit runtime error messages.

Summary by CodeRabbit

  • New Features
    • Configure HTTP request authentication using environment variables without exposing credentials in tool arguments.
    • Interactive setup wizard for credential profile configuration with validation and header conflict detection.
    • Automatic sensitive value redaction in logs and responses.

@theonlyhennygod theonlyhennygod self-assigned this Feb 28, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 28, 2026

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between 6bf9a27 and bba7a0d.

📒 Files selected for processing (6)
  • docs/config-reference.md
  • src/config/mod.rs
  • src/config/schema.rs
  • src/onboard/wizard.rs
  • src/tools/http_request.rs
  • src/tools/mod.rs
 ___________________________________________________________________
< This refactor is like rearranging furniture during an earthquake. >
 -------------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ

✏️ Tip: You can disable in-progress messages and the fortune message in your review settings.

Note

.coderabbit.yaml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key(s) in object: 'tools', 'path_filters', 'review_instructions'
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json
📝 Walkthrough

Walkthrough

Adds env-backed HTTP credential profiles: new HttpRequestCredentialProfile type, credential_profiles map on HttpRequestConfig, validation, interactive wizard setup, and HttpRequestTool runtime support including env-var header injection and sensitive-value redaction.

Changes

Cohort / File(s) Summary
Docs & Config Reference
docs/config-reference.md
Documented new http_request.credential_profiles, examples (TOML and JSON), and usage notes for env-backed header injection.
Config Schema
src/config/schema.rs
Added HttpRequestCredentialProfile (header_name, env_var, value_prefix) and HttpRequestConfig.credential_profiles: HashMap<String, HttpRequestCredentialProfile>; added defaults and validators for profile names, header_name, and env_var; updated tests.
Public Re-exports
src/config/mod.rs
Adjusted public re-exports: removed several prior exports and re-exported HttpRequestCredentialProfile plus a broad set of schema types (see diff). Added test ensuring constructibility of the new profile type.
Onboarding Wizard
src/onboard/wizard.rs
Added interactive flows/utilities: productivity allowlist, CSV domain parsing, env-var validation, profile name normalization, default env var derivation, and setup_http_request_credential_profiles; integrated profile setup into web tool prompts and added tests.
HTTP Tool Implementation
src/tools/http_request.rs
HttpRequestTool gains credential_profiles field; new constructor parameter; added resolve_credential_profile, has_header_name_conflict, and redact_sensitive_values; supports credential_profile arg, injects headers from env, detects header conflicts (case-insensitive), and redacts sensitive headers/body; tests updated.
Tool Initialization
src/tools/mod.rs
Wired http_config.credential_profiles into HttpRequestTool::new calls in all_tools_with_runtime, updating constructor signature usage.

Sequence Diagram

sequenceDiagram
    participant User as User/Wizard
    participant Config as Config Schema
    participant Tool as HttpRequestTool
    participant Env as Environment

    User->>Config: Provide credential_profile name & env_var
    Config->>Config: Validate profile name and env_var
    Config->>Config: Store profile in credential_profiles
    User->>Tool: Execute HTTP request with credential_profile arg
    Tool->>Config: Lookup profile in credential_profiles
    Tool->>Env: Read environment variable
    Env-->>Tool: Return env_var value
    Tool->>Tool: Construct header (header_name, value_prefix + value)
    Tool->>Tool: Check header name conflicts (case-insensitive)
    Tool->>Tool: Inject header and mark value as sensitive
    Tool->>Tool: Execute HTTP request
    Tool->>Tool: Redact sensitive values from response headers and body
    Tool-->>User: Return sanitized response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested labels

tool

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 57.45% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: adding env-backed credential profiles to http_request and improving onboarding with domain policy presets.
Description check ✅ Passed PR description includes all required template sections with substantive content covering problem, changes, validation, security, compatibility, and rollback planning.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch issue-reddit-hardening-onboard-cred-flow

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added docs Auto scope: docs/markdown/template files changed. config Auto scope: src/config/** changed. onboard Auto scope: src/onboard/** changed. tool Auto scope: src/tools/** changed. labels Feb 28, 2026
@github-actions
Copy link
Copy Markdown

github-actions bot commented Feb 28, 2026

PR intake checks found warnings (non-blocking)

Fast safe checks found advisory issues. CI lint/test/build gates still enforce merge quality.

  • Missing required PR template sections: ## Validation Evidence (required), ## Security Impact (required), ## Privacy and Data Hygiene (required), ## Rollback Plan (required)
  • Incomplete required PR template fields: summary problem, summary why it matters, summary what changed, validation commands, security risk/mitigation, privacy status, rollback plan

Action items:

  1. Complete required PR template sections/fields.
  2. Link this PR to exactly one active Linear issue key (RMN-xxx/CDV-xxx/COM-xxx).
  3. Remove tabs, trailing whitespace, and merge conflict markers from added lines.
  4. Re-run local checks before pushing:
    • ./scripts/ci/rust_quality_gate.sh
    • ./scripts/ci/rust_strict_delta_gate.sh
    • ./scripts/ci/docs_quality_gate.sh

Detected Linear keys: RMN-211

Run logs: https://github.com/zeroclaw-labs/zeroclaw/actions/runs/22521271518

Detected blocking line issues (sample):

  • none

Detected advisory line issues (sample):

  • none

@github-actions
Copy link
Copy Markdown

Thanks for contributing to ZeroClaw.

For faster review, please ensure:

  • PR template sections are fully completed
  • cargo fmt --all -- --check, cargo clippy --all-targets -- -D warnings, and cargo test are included
  • If automation/agents were used heavily, add brief workflow notes
  • Scope is focused (prefer one concern per PR)

See CONTRIBUTING.md and docs/pr-workflow.md for full collaboration rules.

@github-actions github-actions bot added size: L Auto size: 501-1000 non-doc changed lines. risk: high Auto risk: security/runtime/gateway/tools/workflows. distinguished contributor Contributor with 50+ merged PRs. config: core Auto module: config core files changed. onboard: wizard Auto module: onboard/wizard changed. provider: openrouter Auto module: provider/openrouter changed. tool: http_request Auto module: tool/http_request changed. and removed config Auto scope: src/config/** changed. onboard Auto scope: src/onboard/** changed. tool Auto scope: src/tools/** changed. labels Feb 28, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (3)
src/config/mod.rs (1)

31-43: Consider adding a constructibility test for HttpRequestCredentialProfile.

The existing tests verify that re-exported config types are constructible. Since HttpRequestCredentialProfile is a new public export central to this PR, consider adding a similar test to ensure the type remains constructible and to document its expected structure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config/mod.rs` around lines 31 - 43, Add a new unit test in the existing
tests module that constructs an HttpRequestCredentialProfile and asserts its
fields are initialized as expected; for example create a test function named
reexported_http_request_credential_profile_is_constructible that either calls
HttpRequestCredentialProfile::default() or constructs it directly and verifies
key public fields (e.g., that optional fields are Some/None or strings are
non-empty as appropriate) to ensure the type is constructible and exported
correctly.
src/tools/http_request.rs (1)

296-297: Validate credential_profile type explicitly when the key is present.

If credential_profile is provided with a non-string value, it is silently ignored today. Returning a structured error keeps parameter validation strict and predictable.

🧭 Proposed fix
-        let credential_profile = args.get("credential_profile").and_then(|v| v.as_str());
+        let credential_profile = match args.get("credential_profile") {
+            Some(value) => match value.as_str() {
+                Some(name) => Some(name),
+                None => {
+                    return Ok(ToolResult {
+                        success: false,
+                        output: String::new(),
+                        error: Some("Invalid 'credential_profile': expected string".into()),
+                    });
+                }
+            },
+            None => None,
+        };

As per coding guidelines src/tools/**/*.rs: "Implement Tool trait ... with strict parameter schema, validate and sanitize all inputs."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tools/http_request.rs` around lines 296 - 297, The code currently
silently ignores non-string credential_profile values; update the argument
validation around args.get("credential_profile") (where credential_profile is
set) to explicitly check the value's type and return a structured error when the
key exists but is not a string (similar to how body is handled), e.g., detect
presence with args.contains_key("credential_profile") then confirm
args.get(...).and_then(|v| v.as_str()). If the type is wrong, return an Err
describing an invalid parameter (keep the error shape consistent with other
validation errors in this module/Tool implementation) so parameter validation
for credential_profile is strict and predictable.
docs/config-reference.md (1)

552-559: Mention header-conflict behavior in the usage snippet.

Consider adding one sentence that args.headers must not include the same header key as the selected credential_profile (case-insensitive), since runtime rejects that combination.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/config-reference.md` around lines 552 - 559, Add a short sentence to the
http_request usage snippet clarifying header-conflict behavior: state that
args.headers must not include any header key that is also provided by the
selected credential_profile (case-insensitive), because the runtime rejects
requests where a header key duplicates a credential-provided header; reference
the http_request call and the args.headers and credential_profile parameters so
readers know which fields this applies to.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/config/schema.rs`:
- Around line 6331-6340: The current validation for profile.header_name only
checks for control characters; update the validation in the block handling
profile.header_name (the header_name variable coming from
profile.header_name.trim()) to attempt parsing the header name using
reqwest::header::HeaderName::from_bytes(header_name.as_bytes()) and treat any
parse error as a validation failure (bail with a message like
"http_request.credential_profiles.{profile_name}.header_name is invalid:
<error>"). Remove the header_name.chars().any(char::is_control) check and ensure
the new check runs after trimming so invalid tokens (spaces, separators,
non-token chars per RFC 7230) are rejected at config load time.
- Around line 1401-1402: The serde default for the value_prefix field currently
uses #[serde(default)] which deserializes to an empty string; change it to
#[serde(default = "default_http_request_credential_value_prefix")] so omitted
configs get the intended "Bearer " default; ensure the helper function
default_http_request_credential_value_prefix() exists and returns the desired
String and keep the struct's impl Default consistent with that helper (update or
add the helper if missing and reference the field name value_prefix to locate
the change).

In `@src/onboard/wizard.rs`:
- Around line 3085-3088: The code currently falls back to wildcard when
parse_allowed_domains_csv(&raw) returns an empty vector, which unintentionally
broadens access; in the block that checks domains.is_empty() inside wizard.rs
replace the wildcard fallback with a safe handling path — either return an
explicit validation error indicating the custom domain list is empty or return
Ok(vec![]) so no domains are allowed; update any caller/validation logic that
expects a non-empty list accordingly. Ensure you modify the branch around
parse_allowed_domains_csv and domains.is_empty() rather than other validation
paths so the function name and behavior remain consistent.
- Around line 3188-3227: The normalized profile name produced by
normalize_http_request_profile_name may collide with existing keys and the
current http_request_config.credential_profiles.insert call will silently
overwrite; update the code to detect collisions and fail fast: before inserting
the HttpRequestCredentialProfile for profile_name check whether
http_request_config.credential_profiles already contains that key (use
contains_key or the entry API) and if so return an error / anyhow::bail! with a
clear message referencing the original raw_name and normalized profile_name to
prevent silent overwrites.

In `@src/tools/http_request.rs`:
- Around line 186-194: The function redact_sensitive_values currently skips any
sensitive value shorter than 6 characters, which can leak short secrets; update
redact_sensitive_values to remove the minimum-length gate (delete the
needle.len() < 6 check) but keep the trim and empty-string guard, so every
non-empty trimmed value in sensitive_values is replaced with "***REDACTED***" in
the text; reference the function name redact_sensitive_values and the
sensitive_values iteration to locate and change the logic.

---

Nitpick comments:
In `@docs/config-reference.md`:
- Around line 552-559: Add a short sentence to the http_request usage snippet
clarifying header-conflict behavior: state that args.headers must not include
any header key that is also provided by the selected credential_profile
(case-insensitive), because the runtime rejects requests where a header key
duplicates a credential-provided header; reference the http_request call and the
args.headers and credential_profile parameters so readers know which fields this
applies to.

In `@src/config/mod.rs`:
- Around line 31-43: Add a new unit test in the existing tests module that
constructs an HttpRequestCredentialProfile and asserts its fields are
initialized as expected; for example create a test function named
reexported_http_request_credential_profile_is_constructible that either calls
HttpRequestCredentialProfile::default() or constructs it directly and verifies
key public fields (e.g., that optional fields are Some/None or strings are
non-empty as appropriate) to ensure the type is constructible and exported
correctly.

In `@src/tools/http_request.rs`:
- Around line 296-297: The code currently silently ignores non-string
credential_profile values; update the argument validation around
args.get("credential_profile") (where credential_profile is set) to explicitly
check the value's type and return a structured error when the key exists but is
not a string (similar to how body is handled), e.g., detect presence with
args.contains_key("credential_profile") then confirm args.get(...).and_then(|v|
v.as_str()). If the type is wrong, return an Err describing an invalid parameter
(keep the error shape consistent with other validation errors in this
module/Tool implementation) so parameter validation for credential_profile is
strict and predictable.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 684503f and ab59111.

📒 Files selected for processing (6)
  • docs/config-reference.md
  • src/config/mod.rs
  • src/config/schema.rs
  • src/onboard/wizard.rs
  • src/tools/http_request.rs
  • src/tools/mod.rs

Comment thread src/config/schema.rs
Comment thread src/config/schema.rs
Comment thread src/onboard/wizard.rs
Comment on lines +3085 to +3088
let domains = parse_allowed_domains_csv(&raw);
if domains.is_empty() {
Ok(vec!["*".to_string()])
} else {
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.

⚠️ Potential issue | 🟠 Major

Avoid silently escalating to wildcard when custom domain input is empty.

At Line 3087, an empty custom list falls back to *, which unexpectedly broadens access to all domains after the user explicitly chose “Custom domain list”.

🔒 Proposed fix
                 let domains = parse_allowed_domains_csv(&raw);
                 if domains.is_empty() {
-                    Ok(vec!["*".to_string()])
+                    anyhow::bail!(
+                        "Custom domain list cannot be empty. Use 'Allow all public domains (*)' if that is intended."
+                    )
                 } else {
                     Ok(domains)
                 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let domains = parse_allowed_domains_csv(&raw);
if domains.is_empty() {
Ok(vec!["*".to_string()])
} else {
let domains = parse_allowed_domains_csv(&raw);
if domains.is_empty() {
anyhow::bail!(
"Custom domain list cannot be empty. Use 'Allow all public domains (*)' if that is intended."
)
} else {
Ok(domains)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/onboard/wizard.rs` around lines 3085 - 3088, The code currently falls
back to wildcard when parse_allowed_domains_csv(&raw) returns an empty vector,
which unintentionally broadens access; in the block that checks
domains.is_empty() inside wizard.rs replace the wildcard fallback with a safe
handling path — either return an explicit validation error indicating the custom
domain list is empty or return Ok(vec![]) so no domains are allowed; update any
caller/validation logic that expects a non-empty list accordingly. Ensure you
modify the branch around parse_allowed_domains_csv and domains.is_empty() rather
than other validation paths so the function name and behavior remain consistent.

Comment thread src/onboard/wizard.rs
Comment thread src/tools/http_request.rs
Comment on lines +186 to +194
fn redact_sensitive_values(text: &str, sensitive_values: &[String]) -> String {
let mut redacted = text.to_string();
for value in sensitive_values {
let needle = value.trim();
if needle.is_empty() || needle.len() < 6 {
continue;
}
redacted = redacted.replace(needle, "***REDACTED***");
}
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.

⚠️ Potential issue | 🟠 Major

Remove the minimum-length redaction gate to avoid leaking short secrets.

Values shorter than 6 characters are currently skipped, which can expose valid credentials (short API keys/tokens) in output.

🔐 Proposed fix
     for value in sensitive_values {
         let needle = value.trim();
-        if needle.is_empty() || needle.len() < 6 {
+        if needle.is_empty() {
             continue;
         }
         redacted = redacted.replace(needle, "***REDACTED***");
     }

As per coding guidelines src/{security,runtime,gateway,tools,observability}/**/*.rs: "never log secrets, raw tokens, or sensitive payloads."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tools/http_request.rs` around lines 186 - 194, The function
redact_sensitive_values currently skips any sensitive value shorter than 6
characters, which can leak short secrets; update redact_sensitive_values to
remove the minimum-length gate (delete the needle.len() < 6 check) but keep the
trim and empty-string guard, so every non-empty trimmed value in
sensitive_values is replaced with "***REDACTED***" in the text; reference the
function name redact_sensitive_values and the sensitive_values iteration to
locate and change the logic.

@theonlyhennygod theonlyhennygod force-pushed the issue-reddit-hardening-onboard-cred-flow branch from ab59111 to 6bf9a27 Compare February 28, 2026 07:06
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (2)
src/onboard/wizard.rs (2)

3269-3276: ⚠️ Potential issue | 🟡 Minor

Guard against normalized profile-name collisions before insert.

At Line 3269, insert can silently overwrite an existing profile if two raw names normalize to the same key.

🛠️ Suggested fix
+        if http_request_config
+            .credential_profiles
+            .contains_key(&profile_name)
+        {
+            anyhow::bail!(
+                "Credential profile '{}' normalizes to '{}' which already exists. Choose a different profile name.",
+                raw_name,
+                profile_name
+            );
+        }
+
         http_request_config.credential_profiles.insert(
             profile_name.clone(),
             HttpRequestCredentialProfile {
                 header_name,
                 env_var,
                 value_prefix,
             },
         );

Based on learnings, "Keep error paths obvious and localized; prefer explicit bail!/errors for unsupported or unsafe states; never silently broaden permissions/capabilities".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/onboard/wizard.rs` around lines 3269 - 3276, The code currently calls
http_request_config.credential_profiles.insert(profile_name.clone(),
HttpRequestCredentialProfile { header_name, env_var, value_prefix }) which can
silently overwrite an existing entry when two raw profile names normalize to the
same key; modify the logic to check for an existing key before inserting (e.g.,
use contains_key or entry API), and if a collision is detected return an
explicit error/bail (with a descriptive message including profile_name) instead
of overwriting; ensure this check happens immediately before inserting into
credential_profiles and reference the HttpRequestCredentialProfile construction
so you only insert after the collision guard passes.

3134-3137: ⚠️ Potential issue | 🟠 Major

Do not widen custom HTTP domain policy to wildcard on empty input.

At Line 3135, empty custom input currently resolves to *, which silently broadens permissions right after the operator chose “Custom domain list.”

🔒 Suggested fix
                 let domains = parse_allowed_domains_csv(&raw);
                 if domains.is_empty() {
-                    Ok(vec!["*".to_string()])
+                    anyhow::bail!(
+                        "Custom domain list cannot be empty. Use 'Allow all public domains (*)' if that is intended."
+                    )
                 } else {
                     Ok(domains)
                 }

As per coding guidelines, "Prefer explicit bail!/errors for unsupported or unsafe states; never silently broaden permissions/capabilities".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/onboard/wizard.rs` around lines 3134 - 3137, The current logic in the
onboarding flow uses parse_allowed_domains_csv(&raw) and returns
Ok(vec!["*".to_string()]) when the parsed domains list is empty, which silently
broadens the HTTP domain policy; change this to treat an empty custom input as
an error instead: when parse_allowed_domains_csv(&raw) yields an empty Vec,
return an explicit error (e.g., bail! or Err with a clear message) indicating
that a custom domain list cannot be empty rather than substituting the wildcard;
update the surrounding function (the onboarding/wizard handler that calls
parse_allowed_domains_csv) to propagate this error so callers can surface it to
the user.
🧹 Nitpick comments (1)
src/tools/http_request.rs (1)

807-839: Consider using a dedicated test environment variable instead of HOME.

While using HOME works reliably, consider setting a project-scoped test env var (e.g., ZEROCLAW_TEST_CREDENTIAL_VALUE) at the start of the test. This avoids any risk of real user paths appearing in failure output and aligns with the principle of keeping test fixtures project-scoped.

♻️ Optional: Use dedicated test env var
     #[test]
     fn resolve_credential_profile_injects_env_backed_header() {
+        let test_secret = "test-credential-value-12345";
+        std::env::set_var("ZEROCLAW_TEST_HTTP_CREDENTIAL", test_secret);
+
         let mut profiles = HashMap::new();
         profiles.insert(
             "github".to_string(),
             HttpRequestCredentialProfile {
                 header_name: "Authorization".to_string(),
-                env_var: "HOME".to_string(),
+                env_var: "ZEROCLAW_TEST_HTTP_CREDENTIAL".to_string(),
                 value_prefix: "Bearer ".to_string(),
             },
         );
 
         // ... tool construction ...
 
-        let home = std::env::var("HOME").expect("HOME should be set in test environment");
         let (headers, sensitive_values) = tool
             .resolve_credential_profile("github")
             .expect("profile should resolve");
 
         assert_eq!(headers.len(), 1);
         assert_eq!(headers[0].0, "Authorization");
-        assert_eq!(headers[0].1, format!("Bearer {home}"));
-        assert!(sensitive_values.contains(&home));
-        assert!(sensitive_values.contains(&format!("Bearer {home}")));
+        assert_eq!(headers[0].1, format!("Bearer {test_secret}"));
+        assert!(sensitive_values.contains(&test_secret.to_string()));
+        assert!(sensitive_values.contains(&format!("Bearer {test_secret}")));
+
+        std::env::remove_var("ZEROCLAW_TEST_HTTP_CREDENTIAL");
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/tools/http_request.rs` around lines 807 - 839, The test
resolve_credential_profile_injects_env_backed_header should not rely on HOME;
set a dedicated test env var (e.g., "ZEROCLAW_TEST_CREDENTIAL_VALUE") at the
start of the test, update the HttpRequestCredentialProfile.env_var to that name,
and use std::env::set_var to seed it with a deterministic value then read it via
std::env::var as before; after the assertions, clean up with
std::env::remove_var to avoid test pollution. Locate the test function
resolve_credential_profile_injects_env_backed_header, the
HttpRequestCredentialProfile creation, and the call to
tool.resolve_credential_profile to apply these changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/onboard/wizard.rs`:
- Around line 3269-3276: The code currently calls
http_request_config.credential_profiles.insert(profile_name.clone(),
HttpRequestCredentialProfile { header_name, env_var, value_prefix }) which can
silently overwrite an existing entry when two raw profile names normalize to the
same key; modify the logic to check for an existing key before inserting (e.g.,
use contains_key or entry API), and if a collision is detected return an
explicit error/bail (with a descriptive message including profile_name) instead
of overwriting; ensure this check happens immediately before inserting into
credential_profiles and reference the HttpRequestCredentialProfile construction
so you only insert after the collision guard passes.
- Around line 3134-3137: The current logic in the onboarding flow uses
parse_allowed_domains_csv(&raw) and returns Ok(vec!["*".to_string()]) when the
parsed domains list is empty, which silently broadens the HTTP domain policy;
change this to treat an empty custom input as an error instead: when
parse_allowed_domains_csv(&raw) yields an empty Vec, return an explicit error
(e.g., bail! or Err with a clear message) indicating that a custom domain list
cannot be empty rather than substituting the wildcard; update the surrounding
function (the onboarding/wizard handler that calls parse_allowed_domains_csv) to
propagate this error so callers can surface it to the user.

---

Nitpick comments:
In `@src/tools/http_request.rs`:
- Around line 807-839: The test
resolve_credential_profile_injects_env_backed_header should not rely on HOME;
set a dedicated test env var (e.g., "ZEROCLAW_TEST_CREDENTIAL_VALUE") at the
start of the test, update the HttpRequestCredentialProfile.env_var to that name,
and use std::env::set_var to seed it with a deterministic value then read it via
std::env::var as before; after the assertions, clean up with
std::env::remove_var to avoid test pollution. Locate the test function
resolve_credential_profile_injects_env_backed_header, the
HttpRequestCredentialProfile creation, and the call to
tool.resolve_credential_profile to apply these changes.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ab59111 and 6bf9a27.

📒 Files selected for processing (6)
  • docs/config-reference.md
  • src/config/mod.rs
  • src/config/schema.rs
  • src/onboard/wizard.rs
  • src/tools/http_request.rs
  • src/tools/mod.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • docs/config-reference.md
  • src/config/schema.rs

@linear
Copy link
Copy Markdown

linear bot commented Feb 28, 2026

@github-actions github-actions bot added config Auto scope: src/config/** changed. onboard Auto scope: src/onboard/** changed. tool Auto scope: src/tools/** changed. and removed config Auto scope: src/config/** changed. onboard Auto scope: src/onboard/** changed. tool Auto scope: src/tools/** changed. labels Feb 28, 2026
@theonlyhennygod theonlyhennygod force-pushed the issue-reddit-hardening-onboard-cred-flow branch from 6bf9a27 to bba7a0d Compare February 28, 2026 13:02
@github-actions github-actions bot added config Auto scope: src/config/** changed. onboard Auto scope: src/onboard/** changed. tool Auto scope: src/tools/** changed. and removed config Auto scope: src/config/** changed. onboard Auto scope: src/onboard/** changed. tool Auto scope: src/tools/** changed. labels Feb 28, 2026
@theonlyhennygod theonlyhennygod merged commit f0a5bbd into main Feb 28, 2026
2 of 5 checks passed
@theonlyhennygod theonlyhennygod deleted the issue-reddit-hardening-onboard-cred-flow branch February 28, 2026 13:07
@gh-xj gh-xj mentioned this pull request Mar 2, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

config: core Auto module: config core files changed. distinguished contributor Contributor with 50+ merged PRs. docs Auto scope: docs/markdown/template files changed. onboard: wizard Auto module: onboard/wizard changed. provider: openrouter Auto module: provider/openrouter changed. risk: high Auto risk: security/runtime/gateway/tools/workflows. size: L Auto size: 501-1000 non-doc changed lines. tool: http_request Auto module: tool/http_request changed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant