Skip to content

Code Review Findings - #5

Open
JuliDi wants to merge 15 commits into
mainfrom
julian/code-review-findings
Open

Code Review Findings#5
JuliDi wants to merge 15 commits into
mainfrom
julian/code-review-findings

Conversation

@JuliDi

@JuliDi JuliDi commented Aug 4, 2026

Copy link
Copy Markdown
Member

I performed a code review with Claude and it found a few things.

This PR implements fixes (approx. 1 commit per fix) and improves both safety and ergonomics of this crate.

JuliDi added 15 commits August 4, 2026 09:30
There is no way to point the client at a proxy and no way to change the
destination because
https://challenges.cloudflare.com/turnstile/v0/siteverify is hardcoded.
It was named/renamed to remote_ip which is not what cloudflare expects.
Also add skip_serializing_if for Option::is_none, so None/nil fields are
skipped in the json payload.
Also drop the `secret` field, it is now populated from the turnstile
client. See
https://developers.cloudflare.com/turnstile/get-started/server-side-validation/
Before it was necessary to check the success field, which can easily be
missed by the caller. It is better to return an error, because one
usually expects the verification to pass and everything else can be
considered an error
Right now, a malicious response might cause us to buffer a huge response
message (depending on any internal limitations of reqwest or so). Since
we expect only a couple of hundred bytes, we limit it.
Right now, if cloudflare adds a new error code, this prematurely errors
in the parser, losing some more info from the response.
This introduces a catchall and removes some old error codes that are no
longer used by cloudflare.
Also update broken link
test_fail only asserts is_err(). A DNS failure, a TLS handshake failure, a timeout, or a SerdeError from an unparseable body all satisfy it equally; so if valid rejections regressed into parse errors, the test would still pass
Cargo features are additive, so we must allow multiple tls backends
so that when cf-turnstile is pulled in to the dependency graph twice
with different TLS backends, it must still compile.
Run taplo fmt on the toml files
And update the docs accordingly, mentioning the idempotency feature
@JuliDi
JuliDi requested a review from joelsa August 4, 2026 13:33
@JuliDi JuliDi self-assigned this Aug 4, 2026
Comment thread src/lib.rs
return Err(TurnstileError::UnexpectedStatus(status));
}

let body_bytes = match Limited::new(response.into_body(), MAX_RESPONSE_BYTES)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think limiting this is smart, but maybe should it also be wrapped in a tokio timeout? Otherwise a peer could send headers but never finish.

Comment thread src/lib.rs
Comment on lines +169 to +171
// The serialized body contains the secret key. Hand `Bytes` a zeroizing owner
// so the buffer is wiped when the request is done rather than merely freed,
// which would leave the key readable in a core dump or swapped-out page.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this over-promises, a core dump at the right time would still contain the key, because the Zeroize Wrapper is only created once the serialization succeeds. I would just state in the comment, that this is best-practive yet best-effort.

Comment thread src/lib.rs
let http =
hyper_util::client::legacy::Client::builder(TokioExecutor::new()).build(connector);

Self { http, secret }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
Self { secret, http }

Comment thread Cargo.toml
missing_docs = "warn"

[lints.clippy]
all = "deny"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What do you think of this?

Suggested change
all = "deny"
all = "deny"
pedantic = { level = "deny", priority = -1 }
nursery = { level = "deny", priority = -1 }

Currently this does not pass with 10 findings, but they are easily fixable and I like the "umm, actually" behavior of pedantic clippy.

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.

2 participants