Skip to content

fix: remove broken createNonceGetter() (v1.0.1)#8

Merged
Enalmada merged 6 commits intomainfrom
fix/remove-createNonceGetter-v1.0.1
Nov 2, 2025
Merged

fix: remove broken createNonceGetter() (v1.0.1)#8
Enalmada merged 6 commits intomainfrom
fix/remove-createNonceGetter-v1.0.1

Conversation

@Enalmada
Copy link
Owner

@Enalmada Enalmada commented Nov 2, 2025

Summary

Removes broken createNonceGetter() function and updates documentation with official TanStack Router pattern.

Changes

Breaking Change

  • Removed: createNonceGetter() function (had critical AsyncLocalStorage bug)
  • Why: The createIsomorphicFn() wrapper broke AsyncLocalStorage context chain
  • Migration: See docs/MIGRATION-1.0-to-1.0.1.md

Bug Fixes

  • Fixed: CSP warnings by preventing 'unsafe-eval' from being copied to script-src-elem
  • Fixed: Removed redundant directives ignored by 'strict-dynamic'
  • Result: Zero browser console warnings

Documentation

  • Updated: README with official TanStack pattern (direct context access)
  • Updated: Security Model section to match actual implementation
  • Added: Comprehensive migration guide
  • Added: Deprecation notice in API docs

What Still Works

No changes to these (all work perfectly):

  • createCspMiddleware() - Middleware nonce generation
  • generateNonce() - Crypto-random nonce generation
  • buildCspHeader() - CSP header building
  • ✅ All security headers and CSP rules

Migration Path

Before (v1.0.0 - BROKEN):

import { createNonceGetter } from '@enalmada/start-secure';
const getNonce = createNonceGetter();
const router = createRouter({ ssr: { nonce: getNonce() } });

After (v1.0.1 - WORKING):

export async function getRouter() {
  let nonce: string | undefined;
  if (typeof window === 'undefined') {
    const { getStartContext } = await import('@tanstack/start-storage-context');
    nonce = getStartContext().contextAfterGlobalMiddlewares?.nonce;
  }
  return createRouter({ ssr: { nonce } });
}

Testing

  • ✅ Built and tested in TanStarter integration
  • ✅ Zero CSP violations
  • ✅ Zero console warnings
  • ✅ All quality checks passing

References

Enalmada and others added 6 commits November 2, 2025 12:39
BREAKING CHANGE: Remove createNonceGetter() due to AsyncLocalStorage bug

## What Changed

- **Removed:** createNonceGetter() function (broken isomorphic wrapper)
- **Updated:** src/nonce.ts with deprecation notice and correct pattern
- **Updated:** src/index.ts exports (removed createNonceGetter export)
- **Updated:** README with v1.0.1 pattern (direct context access)
- **Added:** Comprehensive migration guide (docs/MIGRATION-1.0-to-1.0.1.md)
- **Added:** Changeset for v1.0.1 release

## Why This Change?

The createIsomorphicFn() wrapper broke AsyncLocalStorage context chain:
- Server getStartContext() failed with "No Start context found"
- Scripts rendered without nonce attributes
- All scripts blocked by CSP

## Fixed by

Using direct context access (official TanStack pattern):

```typescript
export async function getRouter() {
  let nonce: string | undefined;
  if (typeof window === 'undefined') {
    const { getStartContext } = await import('@tanstack/start-storage-context');
    nonce = getStartContext().contextAfterGlobalMiddlewares?.nonce;
  }
  return createRouter({ ssr: { nonce } });
}
```

## Migration

See docs/MIGRATION-1.0-to-1.0.1.md for complete guide.

## Quality Checks

✅ TypeScript compilation - PASSED
✅ Linting (Biome) - PASSED
✅ Tests (28 tests) - PASSED

Reference: TanStack/router#3028
Removed all specific version references (v0.1, v0.2, v1.0.0, v1.0.1) from README.
Makes documentation version-agnostic and easier to maintain.

Changes:
- Replaced version-specific sections with descriptive names
- 'v0.2 API' → 'Middleware API'
- 'v0.1' → 'Handler Wrapper - Deprecated'
- Removed version numbers from features list
- Updated migration section titles
- Kept migration guide link (still useful for users upgrading)

README now focuses on current best practices without version coupling.
Clean CSP Level 3 implementation that removes directives ignored by browsers
with 'strict-dynamic', eliminating 13 console warnings.

Changes:
- src/internal/csp-builder.ts: Clean script-src directives
  - Remove 'self', 'unsafe-inline', https:, http: (ignored with strict-dynamic)
  - Keep only: nonce, strict-dynamic, unsafe-eval (dev only)

- src/internal/defaults.ts: Matching clean defaults
  - Same pattern as csp-builder for consistency
  - Fallback to old pattern only if no nonce provided

Why these directives are ignored:
- 'self' - Ignored with 'strict-dynamic' (use nonce-based trust)
- 'unsafe-inline' - Ignored when nonce is present (CSP Level 2+)
- https:/http: - Ignored with 'strict-dynamic' (overly permissive)
- URL whitelists - Ignored with 'strict-dynamic' (use nonce chain)

Result:
- 13 CSP warnings → 0 warnings
- Cleaner, standards-compliant CSP
- No loss of functionality (directives were already ignored)

Reference: CSP Level 3 spec - strict-dynamic behavior
According to CSP spec, 'unsafe-eval' applies to eval()/Function() execution,
which is controlled by script-src, not script-src-elem (which controls <script> elements).

This fixes the browser warning: "Ignoring 'unsafe-eval' inside script-src-elem"

Changes:
- csp-builder.ts: Remove 'unsafe-eval' from script-src-elem (keep in script-src)
- defaults.ts: Add script-src-elem directive without 'unsafe-eval'

Result: 3 CSP warnings → 1 CSP warning (only posthog URL whitelist remains)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
The directive copying logic was copying ALL sources from script-src to
script-src-elem, including 'unsafe-eval', which caused browser warnings.

According to CSP spec:
- 'unsafe-eval' controls eval()/Function() execution (script-src)
- script-src-elem only controls <script> element loading
- 'unsafe-eval' in script-src-elem causes "Ignoring 'unsafe-eval'" warning

Fix: Skip 'unsafe-eval' when copying from script-src to script-src-elem

Result: 1 CSP warning → 0 CSP warnings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
The documentation showed CSP directives that don't match the actual code.

Fixed:
- Removed 'self' from script-src examples (ignored with 'strict-dynamic')
- Removed mention of https:/http: in dev mode (also ignored)
- Added dev mode example showing 'unsafe-eval' only in script-src
- Added note that 'unsafe-eval' NOT in script-src-elem (prevents warning)
- Updated CSP Level 3 section with unsafe-eval copying exception

Result: Documentation now accurately reflects zero-warning CSP implementation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@changeset-bot
Copy link

changeset-bot bot commented Nov 2, 2025

🦋 Changeset detected

Latest commit: 672a05b

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@gemini-code-assist
Copy link

Summary of Changes

Hello @Enalmada, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses a critical bug in the createNonceGetter() function that caused issues with AsyncLocalStorage context, leading to broken Content Security Policy (CSP) nonce application. The problematic function has been removed, and the library now adopts the official TanStack Router pattern for nonce retrieval. Additionally, CSP directives have been optimized to eliminate browser console warnings and improve the robustness of the security policy, ensuring a more stable and compliant implementation.

Highlights

  • Breaking Change: createNonceGetter() Removal: The createNonceGetter() function has been removed due to a critical AsyncLocalStorage bug that prevented proper nonce access on the server-side. This is a breaking change, and users must migrate to a new pattern for nonce retrieval.
  • Updated Nonce Retrieval Pattern: The recommended pattern for accessing nonces now aligns with the official TanStack Router approach, involving direct access to the contextAfterGlobalMiddlewares?.nonce within an async getRouter() function. A comprehensive migration guide has been added.
  • CSP Directive Refinements: Content Security Policy (CSP) directives have been refined to prevent 'unsafe-eval' from being copied to script-src-elem (which caused browser warnings) and to remove redundant directives like 'self' and 'unsafe-inline' when 'strict-dynamic' is present, ensuring a cleaner and more effective CSP.
  • Documentation Updates: The README and API documentation have been updated to reflect the changes, including the new nonce access pattern, deprecation notices, and detailed explanations of CSP behavior with strict-dynamic.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request addresses a critical bug in createNonceGetter() by removing the function and aligning with the official TanStack Router pattern for nonce retrieval. The changes are thorough, covering not only the code fix but also significant improvements to the CSP generation logic and comprehensive updates to all documentation, including a new migration guide. The fix for CSP warnings by adjusting how 'strict-dynamic' and 'unsafe-eval' are handled is a great improvement for security and correctness. The documentation updates are excellent and will be very helpful for users migrating to the new version. I have one minor suggestion to improve the example code in the new migration guide for better debuggability.

Comment on lines +181 to +184
} catch (error) {
// Context not available (shouldn't happen, but handle gracefully)
nonce = undefined;
}

Choose a reason for hiding this comment

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

medium

In the complete example, the try...catch block around getStartContext is a good defensive measure. However, silently catching the error and setting nonce to undefined might hide a fundamental setup problem from the developer. If getStartContext fails, it indicates a significant issue with the TanStack Start context. Logging the error would provide valuable debugging information if this unlikely scenario occurs.

Suggested change
} catch (error) {
// Context not available (shouldn't happen, but handle gracefully)
nonce = undefined;
}
} catch (error) {
// Context not available (shouldn't happen, but handle gracefully)
console.error('[start-secure] Failed to get start context for nonce:', error);
nonce = undefined;
}

@Enalmada Enalmada merged commit b083735 into main Nov 2, 2025
3 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.

1 participant

Comments