Skip to content

release: To Prod#1402

Merged
suisuss merged 33 commits into
prodfrom
staging
May 29, 2026
Merged

release: To Prod#1402
suisuss merged 33 commits into
prodfrom
staging

Conversation

@suisuss

@suisuss suisuss commented May 29, 2026

Copy link
Copy Markdown

No description provided.

eskp and others added 27 commits May 26, 2026 12:16
Add CLAUDE.md guidance for the Understand-Anything Claude Code plugin
(install, daily usage, refresh process, repo-specific gotchas) and
gitignore the scratch + dashboard local-diff outputs while keeping
knowledge-graph.json committed so the dashboard works for the next
developer without re-running a full index.
… refs

Bracket indexing already works inside the stored {{@nodeid:Label.field}}
template grammar; only bare references like step2[1] fail. Replace the
cryptic 'only allowed on workflow variables' message with one that points
at the supported grammar, and document tuple/array indexing (and that it
applies in conditions) in the agent-facing template syntax reference.
…ance check

Surface a configure-time warning when a web3/write-contract node calls a
known ERC-20/4626 spend method (transferFrom, redeem, withdrawFrom) and the
workflow has no web3/check-allowance node, so authors catch insufficient
allowance reverts before execution. Pure check in the fast validator (no
network call) so it always runs, not only under opt-in deep validation.
Each action schema now carries an optional outputSchema (JSON Schema)
alongside inputs, synthesized from outputFields or a statically declared
schema, so agents stop probing for output keys. Extracted into a pure,
unit-tested helper.
Recursively map ABI component names onto tuple and tuple-array return values
in read-contract and batch-read-contract via a shared structureAbiResult
helper, so downstream steps read result.liquidityIndex instead of reverse-
engineering positional indices. Also fixes a double-wrapping bug for single
array outputs in batch-read. Builder autocomplete surfaces the nested paths.

BREAKING: web3/read-contract tuple outputs are now named objects, not
positional arrays. Workflows reading result[1] must switch to result.<field>.
…assumption

Strip one array dimension per recursion so tuple[][] structures correctly
instead of flattening the inner dimension. Document that read-contract's
single-output normalization relies on the EVM adapter auto-unwrapping single
outputs.
- Ship initial knowledge graph (.understand-anything/knowledge-graph.json,
  356K, 145 files indexed) so teammates can open /understand-dashboard
  without a full re-index on first use.
- Gitignore meta.json (timestamp churn) and fingerprints.json (per-machine
  incremental cache) to keep PR diffs clean. Commit config.json and
  .understandignore as shared team settings.
- Disable autoUpdate post-commit hook in config.json — it would otherwise
  modify the graph on every commit and pollute feature-PR diffs. Refresh
  cadence (weekly + after big refactors) documented in CLAUDE.md.
- Add orientation pointers to /add-protocol, /fix-issue, and
  /develop-plugin: spend cheap graph-lookups before dispatching research
  subagents.
- Enable the plugin in .claude/settings.json so teammates with the plugin
  installed see it auto-enabled.
- Document install + usage in README.md (brief; CLAUDE.md has the
  deeper guidance).
…thing-setup

chore: wire Understand-Anything codebase graph into team workflow
KEEP-612 substrate so detection-v0 alerts can ship. Adds three signal
sources and the schema columns behavioral alerts need to group by:

Signal sources:
- Deactivated-login Sentry + structured stdout emit from better-auth
  session/account hooks and the org API-key auth path
- Backstop-trigger reject (ERRCODE 42501) capture wrapping every
  workflow_executions insert; original pg error still propagates
- Pre-execution content scanner for IMDS IPv4, information_schema,
  pg_catalog, neon_auth, refresh_token, client_secret, DATABASE_URL.
  Matched values never leak to alert payloads (verified by test).

Attribution columns on workflow_executions (migration 0088, nullable,
no backfill): triggered_by_user_api_key_id (FK api_keys),
triggered_by_org_api_key_id (FK organization_api_keys),
triggered_by_ip, triggered_by_country (cf-ipcountry), trigger_source.
All four execution insert sites updated to populate.

Alert routing lives in techops_infrastructure (separate PR adds the
Loki rule module).

Tests: 49 passing (35 unit + 14 integration), incl. spec-test that
asserts the deactivated-login capture fires on both session and
account surfaces.

Out of scope (deferred per IMPLEMENTATION_PLAN.md): the behavioral
alert rules themselves, runtime-payload scanning beyond static config,
ASN enrichment beyond country, KeeperhubSecurityPagerduty provisioning,
SAFE_FETCH_ENFORCE flip.
Round-up of the in-branch follow-ups from the post-substrate audit:

- Invitation rate limiter now derives its bucket key from the trusted-
  proxy IP helper (getRequestSourceIp) so a caller can no longer rotate
  `x-forwarded-for` per request to defeat the limit. 7 unit tests cover
  the trusted-CF path and the spoofed-XFF collapse to the shared
  unknown bucket.

- Content scanner now scans triggerInput alongside static node config.
  Each hit carries source: "config" | "trigger_input", deduped by
  (source, nodeId, pattern) so a pattern that appears in both surfaces
  emits two rows. Matched values still never leak to alert payloads.
  Intermediate-node outputs are intentionally not scanned -- too noisy
  and not the attacker entry point.

- New cron endpoint /api/cron/security-behavioral-scan implements the
  first of three behavioral signals: any execution within the last 5
  minutes by a user whose account is < 15 minutes old. Emits a
  structured security.behavioral.new_account_first_workflow log line
  per row. Needs an external scheduler (K8s CronJob) to invoke every
  5 minutes -- terraform is a small follow-up.

- triggered_by_country migration column already populated; design doc
  added at specs/security/asn-enrichment.md comparing three options
  (MaxMind GeoLite2, CF Enterprise header, hybrid) so the team can
  pick when behavioral alerting becomes a priority.

- IMPLEMENTATION_PLAN.md updated with Stage 5 status + the items that
  remain genuinely out of scope (per-owner throughput needs org_slug
  metric label; country-drift needs a per-key country-history table).

Tests: 70 passing across 6 files. No new lint or type errors; the two
pre-existing lint warnings in lib/security/trusted-proxies.ts predate
this branch.

Companion infra PR adds the safe_fetch SSRF blocks Grafana alert and
the new_account_first_workflow Loki alert.
Round-2 self-review fixes for KEEP-612 detection layer.

Critical: register keeperhub_safe_fetch_blocks_total in the Prometheus
collector counterMap. Without this, lib/safe-fetch.ts increments hit
the unknown-metric warn path and the Grafana safe_fetch_blocks_alert
sits at no_data forever.

Medium: tighten the behavioral-cron auth bypass so a configured
CRON_SECRET always wins, even if NODE_ENV is misconfigured to "test"
in prod. Pre-fix logic bypassed unconditionally for dev/test envs;
post-fix only bypasses when no secret is set.

Medium: dual-emit the behavioral-cron signal to Sentry alongside the
structured stdout line, matching the pattern the other security
capture sites use. Triagers now get rich Sentry grouping plus the
durable Loki path.

Cosmetic: swap raw sql-template join for eq() in the cron query.

Tests: extended security-behavioral-scan tests from 5 to 7 -- the new
ones assert the Sentry mirror fires with the expected tag/extra shape
and that a configured CRON_SECRET overrides the dev/test bypass.

Audit doc: reviews/review-20260526-140001-ad204d.md documents the
findings and the rationale for what was/wasn't fixed in this round.
Two post-rebase CI fixes:

- lib/security/content-scanner.ts: TypeScript can't narrow Array.isArray
  on a union member that's a readonly array, so the bare-array branch
  was failing typecheck on CI. Switch to discriminating via the `nodes`
  property using `in`; one cast for the array branch which TS still
  can't narrow without the discriminant.

- Delete tests/unit/invite-fetch-rate-limit.test.ts. It pinned the
  spoofable XFF behaviour that Codex flagged in the first review round.
  The new tests/unit/invitation-rate-limit.test.ts (added in 482d921)
  covers both the now-trusted IP path and the rate-window semantics
  the old file tested, so this is a deletion not a regression.
Drop the message-substring match in withBackstopCapture. The earlier
draft gated on both ERRCODE 42501 AND a fragment match against the
trigger's RAISE text, which silently breaks the detection if the
trigger message is ever reworded. 42501 ("insufficient_privilege")
is rare enough on a workflow_executions INSERT path that the false-
positive risk of dropping the message gate is acceptable.

Also documents that the sessions backstop installed in migration
0090 uses a custom SQLSTATE 'KH001' and fires inside better-auth's
session insert flow, which is intentionally out of scope for this
wrapper -- if/when we add Sentry coverage for that path it lives
in lib/auth.ts around the better-auth call, not here.

Test updated to assert the new ERRCODE-only behaviour.

Addresses PR review item #1.
Adopt the agentic-wallet-sweeper auth pattern: require CRON_SECRET
unconditionally, no NODE_ENV dev/test bypass. The previous logic
allowed unauth'd hits when NODE_ENV was "development" or "test" and
no secret was set -- a prod container booting with NODE_ENV=test
(the misconfig the v2 review flagged) would have opened the endpoint.

Local dev now sets CRON_SECRET in .env to invoke via curl, same as
the existing sweeper. Tests updated: removed the bypass-in-test
assertion, added an explicit "401 when NODE_ENV=test and no secret"
test to pin the misconfig path closed.

Addresses PR review item #2.
The session.create.before and account.create.before hooks both call
captureMessage on a deactivated-user attempt before returning false.
A Sentry transport throw inside either hook would propagate out of
better-auth and surface as a generic login error instead of the
silent deactivated-user deny we want.

All other capture sites in this PR (lib/api-key-auth.ts,
lib/security/backstop-capture.ts, lib/security/content-scanner.ts,
and the behavioral cron) already wrap in try/catch; these two were
the only outliers. Wrapped for consistency.

Addresses PR review item #4.
Expand the doc comment on TriggerSource to explicitly note it is a
strict superset of TriggerType (lib/metrics/types.ts). TriggerType
is the Prometheus-label vocabulary; TriggerSource adds mcp and
internal to distinguish auth paths that the metric label set
conflates -- a workflow with a manual trigger node can be invoked
via the MCP marketplace path OR a direct API call, and the security
attribution column wants to tell them apart even though both report
"manual" to Prometheus.

Adds a guard-rail note that the cast direction
(TriggerType -> TriggerSource) in execute/route.ts is the safe
direction; inverting it would be unsound.

Addresses PR review item #6.
IMPLEMENTATION_PLAN.md is the stage-tracking doc the global CLAUDE.md
philosophy guide instructs to remove once all stages are done. All
stages are done. Reviewer also called these out as PR-meta artifacts
that belong in the PR description or wiki, not in the source tree.

reviews/review-20260526-140001-ad204d.md is the review-loop output
from round 2 of self-review; it served its purpose during iteration
and shouldn't ship in repo.

specs/security/asn-enrichment.md is kept -- per the project CLAUDE.md
convention, specs/ is the correct home for internal design docs
(docs/ is public-facing). It's a durable design artifact, not PR meta.

Addresses PR review item #7.
result[1] no longer works on tuple/struct read-contract outputs - the
new decoder makes result a named object, so the executor throws
"result is not an array" at runtime. Update the actionable validator
error message and the agent-facing TEMPLATE_SYNTAX so they steer
authors toward result.<componentName> for tuples and reserve bracket
indexing for genuine array fields inside the path.
…bstrate

feat(security): detection layer v0 substrate
…t docs

- chains: add status column (stable/experimental/deprecated), default
  stable; 0G mainnet and Galileo seeded as experimental
- mcp: surface per-chain status in list_action_schemas (/api/mcp/schemas)
  and note it in the tool description so agents avoid experimental chains
- dashboard: show the MCP endpoint URL with a copy button in the API
  keys overlay
- docs: add Hackathon Quickstart page consolidating supported chains,
  USDC addresses, faucets, API key types, rate limits, and the MCP endpoint
…rgonomics

feat: improve contract-call and action-schema ergonomics
…chemas

A future refactor of the chains select map in the schemas endpoint would
silently drop the status field with no compile-time signal; this test
locks it in. Three cases: every chain row carries a string status; row
values (stable / experimental) propagate verbatim; the includeChains=false
short-circuit still works (so the new field does not accidentally become
mandatory).

Lives under tests/integration so the existing test-integration CI job
picks it up; no new workflow needed.
…start-matrix

feat: surface per-chain support status, MCP endpoint URL, and hackathon quickstart
suisuss added 3 commits May 29, 2026 13:25
…n scripts

Ethers v6 inlines info.requestUrl (which can include an API key) into
Error.message. Scripts that logged error.message could leak the key on
any RPC failure. The primary leak fired in CI for scripts/seed/seed-tokens.ts
when chain 43114 (AVAX) hit a 403 from a misconfigured Alchemy app.

Add lib/rpc/scrub-rpc-urls.ts (regex-based URL/key scrubber masking known
provider key paths and stripping query strings) and lib/rpc/sanitize-rpc-error.ts
(ethers-aware normalizer that prefers error.shortMessage, falls back to a
scrubbed error.message, and preserves error.code for retry-decision callers).

Apply formatSanitizedRpcError() in six scripts that previously logged
error.message verbatim: seed-tokens.ts, verify-token.ts, register-agent.ts,
update-agent-uri.ts, pin-agent-card.ts, frax-ether-v2-fork-test.ts.

Add 25 unit tests including a structurally faithful ethers v6 SERVER_ERROR
fixture; the critical assertion is that no fake test key marker survives
sanitization.

Operator actions outside this PR: rotate the exposed Alchemy app key and
purge any retained CI logs or artifacts that still hold it.
…-ux-polish

feat: workflow editor UX polish (name field, run-panel tooltips, faucet, workflowType auto-flip)
suisuss added 2 commits May 29, 2026 14:06
…suite

Add an explicit dRPC pattern (lb.drpc.live/<chain>/<key>) so a future
shorter dRPC key still gets caught and the chain segment stays visible
in masked output; previously the generic 32+ char fallback handled it.

Add a regression suite that walks every URL shape currently present in
CHAIN_RPC_CONFIG with fake keys matching real-key charset and length.
If a new provider lands in the config that this scrubber misses, the
corresponding fake key survives and the test fails loudly.

Coverage now includes: TechOps proxy, Flashbots, publicnode, binance,
polygon.technology, arbitrum.io, solana official, tempo.xyz, Ankr public
(all no-secret round-trip); Alchemy v2 (variable key length), Alchemy
wss, dRPC https/wss including dashed chain names, and QuickNode with
and without trailing slash.
…-seed-tokens

fix: sanitize ethers v6 error logs to prevent RPC key leak in scripts
@suisuss suisuss merged commit fe2a367 into prod May 29, 2026
35 checks passed
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.

3 participants