Skip to content

fix(middleware): default HSTS header in production - #2081

Merged
bpamiri merged 1 commit into
developfrom
peter/hsts-default-production
Apr 10, 2026
Merged

fix(middleware): default HSTS header in production#2081
bpamiri merged 1 commit into
developfrom
peter/hsts-default-production

Conversation

@bpamiri

@bpamiri bpamiri commented Apr 10, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • SecurityHeaders middleware now auto-defaults Strict-Transport-Security: max-age=31536000; includeSubDomains when environment is production and no explicit HSTS value is provided
  • Adds environment parameter to init() with fallback to application.$wheels.environment so existing users get HSTS protection without config changes
  • Fully backward compatible: non-production environments and empty environment continue to omit HSTS unless explicitly set

Test plan

  • Verify HSTS auto-defaults when environment="production" and no explicit value
  • Verify HSTS is NOT set for development, testing, or empty environment
  • Verify explicit HSTS values override the auto-default in production
  • Verify backward compatibility: no HSTS when no environment param passed
  • All 2672 tests pass locally (Lucee 7 + SQLite), 0 failures

🤖 Generated with Claude Code

SecurityHeaders middleware now auto-defaults Strict-Transport-Security
to "max-age=31536000; includeSubDomains" when the environment is
production and no explicit HSTS value is provided. Falls back to
checking application.$wheels.environment when no environment parameter
is passed, so existing users get HSTS protection without config changes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@bpamiri
bpamiri merged commit b9df814 into develop Apr 10, 2026
3 checks passed
@bpamiri
bpamiri deleted the peter/hsts-default-production branch April 10, 2026 09:36
@bpamiri bpamiri mentioned this pull request Apr 16, 2026
7 tasks
bpamiri added a commit that referenced this pull request Apr 21, 2026
…ding, glossary (#2181)

* docs(docs): phase 2c plan — ship deployment, contributing, upgrading, glossary

Closes the gap flagged in the post-merge correction on PR #2169: those four
sections shipped as placeholder stubs only, not the content the PR body
described. Plan scopes 14 pages (6 Deployment + 4 Contributing + 3 Upgrading
+ Glossary rewrite) plus sidebar wiring, scoped .ai/ audit, and completion
report.

Scope decisions recorded in the plan:
- Deployment teaches user-authored Dockerfiles + nginx/systemd (no
  wheels docker/wheels deploy commands fabricated — neither exists in v4)
- Kamal port: one sentence + link from deployment/index.mdx to the
  implementation plan, with grep-able TODO to swap to PR URL later
- Dockerfile reference is Lucee 7 canonical; others mentioned in a note
- upgrading/2x-to-3x is a ~1-screen pointer to v3.0 guides
- Glossary hand-curated from Phase 2a/2b terms, ~30-50 entries
- .ai/ audit scoped to Phase 2c topics, not a full sweep

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): deployment/index — landing page for deployment & operations

Explains the two deployment models Wheels supports today (Docker, VM) and the
"what the framework gives you vs what you assemble" boundary. Notes that
wheels deploy (Kamal port) is in active development, with a grep-able TODO
to swap the placeholder for a PR/issue link once one exists.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): deployment/production-config — how to configure wheels for prod

Covers config/environment.cfm + settings.cfm production overrides, env-var
resolution via application.wo.env() (framework → .env file → JVM env), the
auto-flips production applies (showErrorInformation=false, caching on,
autoMigrateDatabase=false), and a 10-item pre-boot checklist with source
anchors in the framework CFCs that enforce each item.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): deployment/docker-deployment — write a production dockerfile

Teaches packaging a wheels app as a docker image. Multi-stage dockerfile
(node builder + lucee/lucee:7-tomcat10-jre21 runtime), .dockerignore,
compose with redis sidecar and required-env guards, split /health + /ready
endpoints, image-size tips, multi-arch buildx, and Adobe/BoxLang base-image
swap hints. Explicit that wheels docker/wheels deploy do not exist in 4.0 —
readers use plain docker and docker compose CLIs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): deployment/vm-deployment — lucee 7 on a linux vm

Covers lucee 7 + java 21 install, /opt/myapp/{current,releases,shared}
release layout, systemd unit (User=lucee, EnvironmentFile, Restart=on-failure),
nginx reverse proxy with TLS termination, and a zero-downtime rsync → migrate
→ ln -sfn swap sequence. CommandBox fallback noted for teams that prefer
box server. No fabricated wheels commands — plain ssh, rsync, systemctl.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): deployment/security-hardening — framework security primitives

Documents every header + setting in vendor/wheels/middleware/SecurityHeaders.cfc
with source line citations, the CSRF enforcement path (authenticityTokenField
→ $runCsrfProtection → $verifyAuthenticityToken), HSTS auto-default in
production, trusted proxy handling, reloadPassword rotation, and secret
management. Flags the missing HSTS off-switch as a known limitation linked
to issue #2174 rather than fabricating a non-existent parameter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): deployment/observability — requestid, sentry, health checks

Covers the RequestId middleware (X-Request-Id, UUID per request), structured
logging patterns, a minimal Health controller with /healthz + /readyz split,
Sentry package activation (cp -r packages/sentry vendor/sentry) with what it
instruments and the known job-error gap, and external APM options (Datadog,
New Relic, OpenTelemetry) since wheels ships no first-party metrics.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): contributing/index — landing page for contributing & project

Explains project governance (core team via git log — no CODEOWNERS file yet),
the four ways to contribute (code, docs, packages, issue triage), and links
the existing CODE_OF_CONDUCT.md (Contributor Covenant v2.0). CardGrid to
pull-requests, coding-standards, writing-docs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): contributing/pull-requests — fork, branch, test, submit

Step-by-step PR workflow. Cites commitlint.config.js directly for the valid
types + scope-enum (23 scopes spanning model, controller, view, router,
middleware, migration, cli, test, config, di, job, mailer, plugin, sse,
seed, docs, web, web/ui, web/landing, web/blog, web/guides, web/api,
web/starlight). Requires bash tools/test-local.sh before push.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): contributing/coding-standards — camelcase + cross-engine compat

Covers camelCase naming, component access modifiers (public for mixin
integration, $-prefix for internal scope), and the top cross-engine gotchas
from .ai/wheels/cross-engine-compatibility.md: struct.map() collision,
Adobe CF application scope, closure this, bracket-notation function calls
in closures, array-by-value in struct literals, createDynamicProxy Lucee
7 semantics, and the private mixin function trap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): contributing/writing-docs — how to author a guide page

Walks a contributor through the docs workflow: frontmatter shapes (howto,
reference, section), Diátaxis typing, the three {test:*} directives sourced
from scripts/verify-docs/VALIDATION.md (compile, cli, tutorial), running
pnpm verify:docs + pnpm build locally, and sidebar wiring via the version
JSON file. Close with the common mdx/cfml gotchas that trip up first-time
contributors.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): upgrading/index — landing with versioning policy

Framework version sourced from vendor/wheels/events/onapplicationstart.cfc
(application.\$wheels.version = 4.0.0). CHANGELOG.md header confirms
Keep-a-Changelog + SemVer. Release cadence described from actual git tag
history (no fabricated schedule). CardGrid to 3x-to-4x (critical) and
2x-to-3x (pointer).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): upgrading/3x-to-4x — the critical upgrade page

Documents every 3.x → 4.0 breaking change with CHANGELOG + PR citations:
CORS deny-all (#2039), HSTS production default (#2081), CSRF key required
(#2079, #2086), allowEnvironmentSwitchViaUrl production flip (#2076),
RateLimiter trustProxy=false (#2024, #2088), CSRF SameSite cookie (#2035),
wheels snippets → wheels code (#1852), wheels.Test → wheels.WheelsTest
(#1889), tests/specs/functions/ → functional/ (#1872), wirebox → wheelsdi
(#1888). Deprecations: plugins/ (#1995), paginationLinks (#1930),
RocketUnit (#1925), HTTP MCP endpoint. Removed: RocketUnit core, Railo
shim (#1987), server.cfc (#1902). Worked example of plugin → package
port with provides.mixins explicit opt-in.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): upgrading/2x-to-3x — pointer to v3 guides

Short pointer (~200 words). Directs readers to the frozen v3.0 guides for
the 2→3 hop and the sibling 3x-to-4x page for the second hop. Explains
why skipping intermediate upgrades is expensive: each major's breaking
changes assume the previous version's semantics.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): glossary — 45 terms harvested from phase 2a/2b pages

Alphabetized reference with one-sentence definitions and links back to
the guide page where each concept is fully explained. Curated from
core-concepts, basics, and digging-deeper pages. Terms cut as too
generic (Controller, View, Model standalone) or lacking a conceptual
anchor in Phase 2a/2b (Mailer, WheelsTest, Strong params). Automation
deferred to a later polish pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): sidebar — wire phase 2c pages into v4 nav

Populates three previously-empty sidebar groups with the new Phase 2c
pages: Deployment & Operations (5 child items), Contributing & Project
(3 child items), Upgrading (2 child items). Glossary entry unchanged —
it's already a single-link row.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): .ai/ audit — delete files superseded by phase 2c

Phase 2c deployment/production-config.mdx and deployment/security-hardening.mdx
now cover the same territory with cited source anchors. Kept
.ai/wheels/security/https-detection.md (scoped to the isSecure() controller
helper, not deployment configuration) and .ai/wheels/cross-engine-compatibility.md
(contributor-facing reference used by contributing/coding-standards.mdx).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs(docs): phase 2c completion report

16 commits, 14 content pages (6 deployment + 4 contributing + 3 upgrading
+ 1 glossary rewrite), sidebar wired, 2 .ai/ files retired. Final build
green at 340 pages; verify:docs green at 43/43 tagged blocks. Per-page
drift captured with source-verification wins and six carryover items
for follow-up work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(docs): address phase 2c final review findings

Three findings from pr-review-toolkit:code-reviewer:

1. upgrading/3x-to-4x.mdx — removed fabricated SecurityHeaders(hsts=false)
   API call. vendor/wheels/middleware/SecurityHeaders.cfc has no hsts boolean;
   real parameter is strictTransportSecurity (string), and empty string
   auto-falls-back to the production default. Page now honestly notes the
   off-switch gap (#2174) with a "strip at your reverse proxy" workaround.

2. deployment/docker-deployment.mdx — fixed broken LinkCard href from
   core-concepts/middleware/ (no such page) to core-concepts/middleware-pipeline/.

3. contributing/coding-standards.mdx — replaced broken LinkCard href pointing
   at non-existent troubleshooting/cross-engine-compatibility/ with a link
   to contributing/pull-requests (sibling page).

Build + verify:docs green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(docs): vm-deployment — correct github org + branch in dockerfile link

Legacy `cfwheels/cfwheels` org and `main` branch — the repo is
`wheels-dev/wheels` and the default branch is `develop`.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bpamiri added a commit that referenced this pull request Jun 10, 2026
…itional gaps (#2941)

U1: wheels upgrade check now throws Wheels.UpgradeCheckFailed after the
report flushes when breaking findings exist (mirrors validate()'s
Wheels.ValidationFailed), so it exits non-zero and can gate CI. Adds
--format=json for a machine-readable report; advisories never affect
the exit code.

U4: breaker scan covers the canonical eleven — the WireBox check now
matches wirebox.system.ioc and scans the root Application.cfc + config/
in addition to app/; new breaking grep for removed renderPage()/
renderPageToString(); new advisories for the HSTS default flip
(SecurityHeaders, #2081) and the CSRF SameSite cookie attribute
(protectsFromForgery, #2035). UpgradeCheckCoverageSpec pins the full
set plus the exit-code throw and --format=json plumbing.

U5: wheels.Testbox shim now documented with a 5.0 removal target (CFC
docblock + 3x-to-4x guide), mapperModernSpec migrated off it to
wheels.WheelsTest, and the upgrade-check test-base-class grep matches
wheels.Testbox and single-quoted extends forms.

U7: renamed the scanTargets loop var that shadowed the target-version
local; fixed the dead 2.x->3.x app/plugins check to the real root
plugins/ path (skipped on 2.x->4.x jumps where the 4.x entry covers
it); breaking-finding output now links the version-appropriate upgrade
guide and the wheels-legacy-adapter soft landing; new in-repo
regression spec for wheels-legacy-adapter registry resolution.

CLI-D5: structuredArgs() now consumes __arguments once (cleared on
every call), so a delegation stash (create/generate app -> new) can
never replay stale argv into a later zero-arg tool call under the
persistent stdio MCP server.

CLI-D1: ArgSpec.parse()/toArgv() bind positionals by collecting and
numerically sorting arg<N> keys (parseTestArgs' proven pattern) instead
of fixed-index probing / stop-at-gap loops. LuCLI numbers positionals
by global token index, so a flag between positionals leaves a gap —
'wheels new --port=3000 blog' previously ignored the app name and
threw, and 'wheels g scaffold Post --force title:string body:text'
silently dropped every column after the flag.

Verified locally (Lucee 7 + SQLite, worktree docker recipe): core cli
specs 78/78, mapperModernSpec 27/27, CLI suite 774 pass with only the
pre-existing environment-dependent SSH/live-server failures.

Signed-off-by: Peter Amiri <peter@alurium.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant