Persist workflows locally - #33
Conversation
| constructor(deps?: Partial<LocalWorkflowStoreDeps>) { | ||
| this.deps = { | ||
| fileSystem: deps?.fileSystem ?? FileSystem.getInstance(), | ||
| devMode: deps?.devMode ?? (() => process.env.NODE_ENV === 'development'), |
There was a problem hiding this comment.
Semgrep identified an issue in your code:
BACKEND-ONLY RULE. process.env.NODE_ENV is NOT reliably set in Uniswap
backend Node.js ECS containers. Pulumi's UniServiceConfig injects
ENV=<stackName> (dev/staging/prod), not NODE_ENV. The runtime Dockerfile
(containers/Dockerfile.ec2) does not set NODE_ENV either.
Comparing NODE_ENV to 'production' or 'development' for security-relevant
decisions (origin allowlists, auth bypasses, rate-limit tuning, feature
flags) falls through to the dev/fallback branch in prod.
Real incident: Cantina finding #669 — WebAuthn origin allowlist in
privy-embedded-wallet used NODE_ENV || 'development', making
*.vercel.app + localhost permanently allowed origins in prod. Enabled
attacker-controlled rp.id in passkey registration, persistent wallet hijack.
Prefer process.env.ENV === 'prod' (see
packages/services/compliancev2/src/dependencies.ts:71) or the dual-read
pattern process.env.ENV || process.env.NODE_ENV || 'local' (see
packages/services/platform-service/src/init.ts:107).
This rule does NOT apply to frontend/universe code. Bundlers (Vite,
Webpack, Metro, WXT, Next.js) statically replace process.env.NODE_ENV at
build time, so the value IS reliable there. Configure Semgrep Cloud to
apply this rule only to the Uniswap backend repo, and keep the path
exclusions below as a second line of defense.
The benign pattern process.env.NODE_ENV !== 'test' for guarding server
auto-start is deliberately NOT matched.
To resolve this comment:
✨ Commit fix suggestion
| devMode: deps?.devMode ?? (() => process.env.NODE_ENV === 'development'), | |
| devMode: deps?.devMode ?? (() => { | |
| const env = process.env.ENV || process.env.NODE_ENV || 'local'; | |
| return env === 'dev'; | |
| }), |
View step-by-step instructions
-
Replace the
NODE_ENVcheck in the defaultdevModeimplementation with the backend-supported environment variable.
Change() => process.env.NODE_ENV === 'development'to() => process.env.ENV === 'dev'. -
Keep the fallback only on the injected dependency, not on
NODE_ENV.
The constructor should continue to preferdeps?.devModefirst, and only use theENV-based default when no override is passed. -
If this code needs to work in more than one runtime that may still set
NODE_ENV, use the approved dual-read pattern instead of readingNODE_ENVdirectly.
Set the environment name withconst env = process.env.ENV || process.env.NODE_ENV || 'local', then checkenv === 'dev'. This avoids falling into a development branch in production whenNODE_ENVis unset.
💬 Ignore this finding
Reply with Semgrep commands to ignore this finding.
/fp <comment>for false positive/ar <comment>for acceptable risk/other <comment>for all other reasons
Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by uniswap-backend-node-env-prod-check.
You can view more details about this finding in the Semgrep AppSec Platform.
david-uniswap
left a comment
There was a problem hiding this comment.
Ran the core workflow tests (87 pass) and the touched frontend suites (31 pass) locally, and read through the store, promotion service, and run-gating changes. The concurrency story holds up: keyed mutex plus hash check inside the write, atomic tmp+rename, and the promotion preview goes stale on doc-hash or profile change. The NUL sentinel for the local pseudo-repo is a nice trick and it's tested for non-collision. LGTM
Workflows can now be saved outside git repos, in a per-profile local storage directory managed by core (profiles//workflows/local/.json).
How it works:
Closes #20.