Skip to content

Return :invalid_option for bad search and summarize options instead of crashing - #48

Merged
rubas-agent[bot] merged 3 commits into
mainfrom
fix-option-validation-search-summary
Oct 16, 2026
Merged

rubas-agent[bot] merged 3 commits into
mainfrom
fix-option-validation-search-summary

Conversation

@rubas-agent

@rubas-agent rubas-agent Bot commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Before

Probe on origin/main, with %Kagi.Client{session_token: "t"}:

{:search, ["x", [site: %{a: 1}]], {:raised, Protocol.UndefinedError}}
{:search, ["x", [region: %{a: 1}]], {:raised, Protocol.UndefinedError}}
{:summarize, ["https://a", [lang: %{}]], {:raised, Protocol.UndefinedError}}
{:search, ["x", [from: "2024-13-45"]], {:error, :http_error, "HTTP 302"}}
{:search, ["x", [verbatim: "false"]], {:error, :http_error, "HTTP 302"}}

The last two reached the network. verbatim: "false" sent verbatim=1.

Problem

AGENTS.md says that a public function without a bang returns {:ok, struct} or {:error, %Kagi.Error{}}. Kagi.search and Kagi.summarize raise on a map in :site, :filetype, :region, or :lang. They also send bad values: any truthy :verbatim turns verbatim on. The date regex accepts "2024-01-01\n", "2024-13-45", and a :from after :to.

After

The same probe on this branch:

{:search, ["x", [site: %{a: 1}]],
 {:error, :invalid_option, ":site must be a non-empty string, got: %{a: 1}"}}
{:search, ["x", [region: %{a: 1}]],
 {:error, :invalid_option, ":region must be a non-empty string, got: %{a: 1}"}}
{:summarize, ["https://a", [lang: %{}]],
 {:error, :invalid_option, ":lang must be a non-empty string, got: %{}"}}
{:search, ["x", [from: "2024-13-45"]],
 {:error, :invalid_option,
  ":from must be a YYYY-MM-DD date, got: \"2024-13-45\""}}
{:search, ["x", [verbatim: "false"]],
 {:error, :invalid_option, ":verbatim must be a boolean, got: \"false\""}}

No call sends a request. The README now says that :from must not be after :to.

Cause, design, tests

Cause.

  • lib/kagi/search.ex:append_filter/3 interpolates :site and :filetype into the query, so a map raises Protocol.UndefinedError.
  • lib/kagi/search.ex:query_params/2 passes :region unchecked to the Req params.
  • lib/kagi/summary.ex:query_params/2 passes :lang unchecked as target_language.
  • lib/kagi/search.ex:validate_date/2 used ~r/^\d{4}-\d{2}-\d{2}$/. $ matches before a trailing newline, and the regex does not check that the date exists.

Design.

  • Kagi.Search.validate_options/1 now runs before build_query/2, so append_filter/3 only sees valid values. One validate_string/2 checks :site, :filetype, and :region (nil or a non-empty binary). validate_verbatim/1 accepts nil or a boolean.
  • parse_date/2 parses :from and :to with Date.from_iso8601/1, and validate_date_order/2 rejects :from after :to. The request still carries the original string. The regex is gone.
  • Kagi.Summary.target_language/1 checks :lang (default "EN").
  • Unknown option keys still pass. Rejecting them is a breaking change for another PR. lib/kagi/http.ex, CHANGELOG.md, and the version do not change.

Tests. test/kagi/search_test.exs:

  • rejects a :site, :filetype, or :region that is not a non-empty string before any request
  • rejects a :verbatim that is not a boolean
  • rejects :from with a trailing newline, an impossible date 2024-13-45, and a range where :from is after :to
  • accepts a valid :from/:to range and sends from_date and to_date unchanged

test/kagi/summary_test.exs: rejects a :lang that is not a non-empty string.

A fake adapter sends a message to the test pid, and refute_received proves that no request went out.

MIX_ENV=test: mix compile --warnings-as-errors, mix format --check-formatted, mix credo --strict, and mix dialyzer are clean. mix test: 59 passed, 3 excluded (live).

rubas added 3 commits October 16, 2026 10:44
…rbatim, and dates

A map or integer in :site, :filetype, or :region raised Protocol.UndefinedError.
A truthy non-boolean :verbatim sent verbatim=1. The date regex let a trailing
newline and impossible dates through, and :from could be after :to.
Validate all of them before the query is built, and parse dates with
Date.from_iso8601/1.
…tring

A map in :lang raised Protocol.UndefinedError when Req encoded the params.
@rubas-agent
rubas-agent Bot merged commit 9944aec into main Oct 16, 2026
1 check passed
@rubas-agent
rubas-agent Bot deleted the fix-option-validation-search-summary branch October 16, 2026 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant