Conversation
ChristianPavilonis
left a comment
There was a problem hiding this comment.
Thanks for the cross-adapter config store work — the overall direction looks good. I’m requesting changes for one high-severity issue plus follow-ups.
Findings
-
High — Axum config store can expose full process environment (secret leakage risk)
crates/edgezero-adapter-axum/src/config_store.rs:12crates/edgezero-adapter-axum/src/config_store.rs:37examples/app-demo/crates/app-demo-core/src/handlers.rs:119AxumConfigStore::from_envsnapshots all env vars, so any handler pattern that accepts user-controlled keys can accidentally expose unrelated secrets.- Requested fix: replace blanket
std::env::vars()capture with an explicit allowlist (manifest-declared keys only), and avoid arbitrary key-lookup patterns in examples intended for production-like usage.
-
Medium — Adapter override key casing is inconsistent across resolution paths
crates/edgezero-core/src/manifest.rs:352crates/edgezero-macros/src/app.rs:120crates/edgezero-core/src/app.rs:59- Mixed-case adapter keys can work in one path and fail in another.
- Requested fix: normalize keys at parse/finalize time (or enforce lowercase with validation) and add a mixed-case adapter-key test.
-
Low — Missing positive-path injection coverage in adapter tests
crates/edgezero-adapter-fastly/tests/contract.rs:17crates/edgezero-adapter-cloudflare/tests/contract.rs:188- Please add success-path assertions that config store injection/retrieval works when bindings are present.
Once the high-severity item is addressed, this should be in good shape.
There was a problem hiding this comment.
Review: Config Store Feature
Overall this is a well-structured feature that follows the existing adapter pattern cleanly. The core trait, contract test macro, per-adapter implementations, and manifest/macro integration are all thoughtfully designed. Test coverage is solid across all three adapters and the docs are thorough.
That said, I found issues across four areas that should be addressed before merge — one high-severity security concern, two medium design issues, and one CI coverage gap.
Summary
| Severity | Finding |
|---|---|
| High | Axum config-store exposes entire process environment (secret leakage risk) |
| Medium | Case handling for adapter overrides is inconsistent between manifest and metadata paths |
| Medium | dispatch() bypasses config-store injection, diverging from run_app behavior |
| Medium-Low | New WASM adapter code paths are weakly exercised in CI |
See inline comments for details and suggested improvements.
ChristianPavilonis
left a comment
There was a problem hiding this comment.
Follow-up review complete. No new issues were found in the current changeset, and previously noted concerns appear addressed.
aram356
left a comment
There was a problem hiding this comment.
PR Review
Summary
Solid config store abstraction with good test coverage across all three adapters. The contract test macro and error mapping are well-designed. Main concerns are around the public dispatch API silently dropping KV handles, and a dual config resolution path that needs clarification.
Findings
Blocking
- 🔧
dispatch_with_config/dispatch_with_config_handlesilently drop KV: Both Fastly and Cloudflare versions passNonefor KV. Users migrating fromdispatchordispatch_with_kvto a config-aware path will lose KV access with no warning. (cloudflarerequest.rs:106-130, fastlyrequest.rs:78-103) - ❓ Dual config name resolution in
run_app: Both adapters checkA::config_store()(compile-time) then fall back to runtime manifest parsing. Since both derive from the sameedgezero.toml, when would these diverge? Needs documentation or removal of dead path. (cloudflarelib.rs:86-94, fastlylib.rs:97-107)
Non-blocking
- 🤔
CloudflareConfigStore::new()silent fallback: A binding typo gives an empty store with only alog::warn. Consider renaming tonew_or_empty()or removing in favor oftry_newonly. - ♻️ Duplicate bounded dedup caches: Fastly
RecentStringSetand CloudflareConfigCacheare structurally identical. Candidate for a shared core utility. - ⛏
_ctxprefix on used variable:cloudflare/tests/contract.rs:55— underscore implies unused but variable is read. - 🏕
wasm-bindgen-testin[dependencies]: Pre-existing but file was touched — test-only crate shouldn't be in production deps. - 🌱 Consider a
ConfigStoreextractor: Handlers callctx.config_store()manually; aConfigStore(store)extractor (likeKv) would complete the pattern.
📌 Out of Scope
wasm-bindgen-testdep placement should be fixed in a separate cleanup PR- Bounded cache dedup is a candidate for future core utility extraction
CI Status
- fmt: PASS
- clippy: PASS
- tests: PASS
| .stores | ||
| .config | ||
| .as_ref() | ||
| .map(|cfg| cfg.config_store_name(edgezero_core::app::CLOUDFLARE_ADAPTER)) |
There was a problem hiding this comment.
❓ Dual config name resolution — when do paths diverge? This first checks A::config_store() (compile-time metadata from the #[app] macro), then falls back to parsing manifest.stores.config at runtime. Since both derive from the same edgezero.toml, these should always agree.
When would A::config_store() return None but the manifest has [stores.config]? Presumably when someone implements Hooks manually without the macro. If so, this should be documented. If not, the fallback is dead code.
Same pattern in edgezero-adapter-fastly/src/lib.rs:97-107.
There was a problem hiding this comment.
The fallback is not dead code: it fires when a caller implements Hooks manually without the #[app] macro, in which case A::config_store() returns None while [stores.config] may still be present in the manifest. Added a comment explaining this in both adapters.
dispatch_with_config and dispatch_with_config_handle in both the Cloudflare and Fastly adapters were passing None for the KV handle, silently dropping KV access for callers on those paths. Both now resolve the default KV binding/store (non-required) alongside the config store. Additional cleanup from review: - Document why run_app has two config-name resolution paths (macro-generated vs. manual Hooks impls) - Rename CloudflareConfigStore::new() to new_or_empty() to make the silent fallback-to-empty behavior explicit - Fix _ctx prefix on an actively-read variable in cloudflare contract tests - Move wasm-bindgen-test to [dev-dependencies]
Summary
ConfigStoreabstraction inedgezero-corethat lets handlers read key/value configuration at runtime without coupling to a specific platform#[app]macro and each adapter's request pipeline so handlers receive aConfigStoreHandleviaRequestContextwith no boilerplateChanges
edgezero-core/src/config_store.rsConfigStoretrait,ConfigStoreHandlewrapper, and shared contract test macroedgezero-core/src/manifest.rsConfigStoreTOML binding and adapter name resolutionedgezero-core/src/context.rsconfig_store()accessor and injection helpers toRequestContextedgezero-core/src/app.rsApp::buildhooks extended to accept config store configurationedgezero-core/src/lib.rsmanifestmoduleedgezero-adapter-axum/src/config_store.rsAxumConfigStorewith defaults supportedgezero-adapter-axum/src/service.rsConfigStoreHandleinto each request before routingedgezero-adapter-axum/src/dev_server.rsDevServerConfigedgezero-adapter-axum/src/lib.rsedgezero-adapter-fastly/src/config_store.rsFastlyConfigStorebacked by Fastly edge dictionaryedgezero-adapter-fastly/src/lib.rsedgezero-adapter-fastly/src/request.rsedgezero-adapter-fastly/tests/contract.rsedgezero-adapter-cloudflare/src/config_store.rsCloudflareConfigStorebacked byworker::Envbindingsedgezero-adapter-cloudflare/src/lib.rsedgezero-adapter-cloudflare/src/request.rsedgezero-adapter-cloudflare/tests/contract.rsedgezero-macros/src/app.rs#[app]macro generates config store setup from manifestexamples/app-demo/docs/guide/scripts/smoke_test_config.sh.gitignoreCloses
Closes #51
Test plan
cargo test --workspace --all-targetscargo clippy --workspace --all-targets --all-features -- -D warningscargo check --workspace --all-targets --features "fastly cloudflare"wasm32-wasip1(Fastly) /wasm32-unknown-unknown(Cloudflare)edgezero-cli devChecklist
{id}syntax (not:id)edgezero_core(nothttpcrate)