Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: unjs/unhead
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.11.20
Choose a base ref
...
head repository: unjs/unhead
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v2.0.0
Choose a head ref

Commits on Jan 7, 2025

  1. fix(core)!: remove HashHydrationPlugin (#444)

    * fix!: remove `HashHydrationPlugin`
    
    * chore: rollback artifacts
    harlan-zw authored Jan 7, 2025
    Copy the full SHA
    fa511d5 View commit details
  2. chore(deps): update all non-major dependencies (#442)

    Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
    renovate[bot] authored Jan 7, 2025
    Copy the full SHA
    b5ba59d View commit details
  3. feat(core)!: unctx fallback context (#437)

    * feat!: Unhead Context
    
    * chore: prefer exports from `unhead`
    
    * fix: set context client-side
    
    * fix: non-union returns
    
    * chore: include updated tests
    
    * chore: sync lock
    
    * chore: migrate
    
    * chore: sync lock
    harlan-zw authored Jan 7, 2025
    Copy the full SHA
    eb6ff33 View commit details
  4. fix(vue)!: drop Vue 2 support (#439)

    * fix!: drop Vue 2 support
    
    * chore: missed
    
    * chore: useless test
    
    * chore: sync lock
    
    * chore: conflicts
    
    * doc: update for Vue 2
    harlan-zw authored Jan 7, 2025
    Copy the full SHA
    838a713 View commit details
  5. feat(core)!: default capo sorting (#440)

    * feat!: default capo sorting
    
    * chore: fixing tests
    
    * chore: fixing tests
    
    * chore: simplify
    harlan-zw authored Jan 7, 2025
    Copy the full SHA
    889e61a View commit details
  6. fix!: only handle promise input when opted in (#445)

    * fix!: drop promise input support
    
    * chore: missing build artifacts
    
    * chore: artifact
    harlan-zw authored Jan 7, 2025
    Copy the full SHA
    af0afbf View commit details
  7. refactor: linting fixes & misc clean up (#446)

    * refactor: linting fixes & misc clean up
    
    * chore: broken
    harlan-zw authored Jan 7, 2025
    Copy the full SHA
    a6c5bf1 View commit details
  8. fix(core)!: drop vmid, hid, children body keys (#447)

    * fix!: drop `vmid`, `hid`, `children` `body` keys
    
    * fix: handle `body` and `children`
    
    * fix: dupe
    harlan-zw authored Jan 7, 2025
    Copy the full SHA
    e1f44e9 View commit details
  9. Copy the full SHA
    709144e View commit details

Commits on Jan 8, 2025

  1. perf!: client / server subpaths (#448)

    * perf!: client / server subpaths
    
    * chore: keep `createHeadCore` export, add `unhead/legacy`
    harlan-zw authored Jan 8, 2025
    Copy the full SHA
    49634d3 View commit details
  2. fix: export InferSeoMetaPlugin from unhead/optionalPlugins (#449)

    * fix: export `InferSeoMetaPlugin` from `unhead/optionalPlugins`
    
    * chore: fix build
    harlan-zw authored Jan 8, 2025
    Copy the full SHA
    13db811 View commit details
  3. feat: vanilla function resolves (#443)

    * feat: vanilla function resolves
    
    Fixes #435
    
    * chore: broken test
    harlan-zw authored Jan 8, 2025
    Copy the full SHA
    b2ed420 View commit details
  4. perf: isolate plugin logic (#451)

    * feat: vanilla function resolves
    
    Fixes #435
    
    * chore: broken test
    
    * perf: isolate plugin logic
    harlan-zw authored Jan 8, 2025
    Copy the full SHA
    66a4661 View commit details
  5. chore(deps): update devdependency unplugin-vue-components to v28 (#450)

    Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
    renovate[bot] authored Jan 8, 2025
    Copy the full SHA
    d6bfebf View commit details

Commits on Jan 9, 2025

  1. feat(scripts)!: useScript overhaul, @unhead/scripts (#436)

    * chore: release v1.11.14
    
    * progress commit
    
    * chore: progress commit
    
    * chore: broken tests
    
    * chore: fix build
    
    * chore: fix build
    
    * doc: install fix
    
    * fix: no longer augment as promise, export legacy
    
    * fix: use forwarding proxy once loaded
    
    * chore: read me
    
    * feat: support event deduping
    harlan-zw authored Jan 9, 2025
    Copy the full SHA
    c234e21 View commit details

Commits on Jan 13, 2025

  1. chore: legacy exports and misc tech debt (#457)

    * chore: fix legacy exports
    
    * chore: fix legacy exports
    
    * chore: bump lock
    
    * chore: broken tests
    
    * chore: script legacy exports
    
    * chore: tidy up
    
    * chore: tidy up
    
    * chore: bump
    
    * chore: bump
    harlan-zw authored Jan 13, 2025
    Copy the full SHA
    7ae12ce View commit details
  2. chore: export missing functions (#458)

    * chore: export missing fuinctions
    
    * chore: fix circular deps
    
    * chore: `next` releases
    harlan-zw authored Jan 13, 2025
    Copy the full SHA
    7ffb062 View commit details
  3. Copy the full SHA
    85b58b0 View commit details
  4. chore: npm meta data

    harlan-zw committed Jan 13, 2025
    Copy the full SHA
    447a0b9 View commit details
  5. Copy the full SHA
    7550484 View commit details
  6. chore: npm meta data

    harlan-zw committed Jan 13, 2025
    Copy the full SHA
    f3fd8b5 View commit details
  7. chore: npm meta data

    harlan-zw committed Jan 13, 2025
    Copy the full SHA
    501c9e2 View commit details
  8. Copy the full SHA
    7fc779d View commit details
  9. Copy the full SHA
    807da07 View commit details

Commits on Jan 18, 2025

  1. chore(deps): update all non-major dependencies (#456)

    Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
    renovate[bot] authored Jan 18, 2025
    Copy the full SHA
    7b47439 View commit details

Commits on Jan 21, 2025

  1. Copy the full SHA
    927f1b4 View commit details
  2. Copy the full SHA
    f17d33e View commit details
  3. Copy the full SHA
    8cb562d View commit details
  4. Copy the full SHA
    5f7c311 View commit details
  5. chore(vue): missing types

    harlan-zw committed Jan 21, 2025
    Copy the full SHA
    4f8eb51 View commit details
  6. Copy the full SHA
    e2245af View commit details
  7. Copy the full SHA
    7c6244e View commit details
  8. chore: bump lock

    harlan-zw committed Jan 21, 2025
    Copy the full SHA
    8780df4 View commit details
  9. Copy the full SHA
    e676153 View commit details
  10. Copy the full SHA
    c629670 View commit details
  11. Copy the full SHA
    fa37df1 View commit details

Commits on Jan 22, 2025

  1. chore: clean up

    harlan-zw committed Jan 22, 2025
    Copy the full SHA
    9943cd8 View commit details
  2. chore: clean up

    harlan-zw committed Jan 22, 2025
    Copy the full SHA
    c514bfc View commit details
  3. chore: type regression

    harlan-zw committed Jan 22, 2025
    Copy the full SHA
    1de94c4 View commit details
  4. chore: broken build

    harlan-zw committed Jan 22, 2025
    Copy the full SHA
    3c261e0 View commit details
  5. Copy the full SHA
    00192ba View commit details

Commits on Jan 23, 2025

  1. Copy the full SHA
    86ac404 View commit details

Commits on Jan 26, 2025

  1. chore(deps): update all non-major dependencies (#464)

    Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
    renovate[bot] authored Jan 26, 2025
    Copy the full SHA
    3114f60 View commit details
  2. feat: Angular support

    harlan-zw committed Jan 26, 2025
    Copy the full SHA
    0e5c2a9 View commit details
  3. Copy the full SHA
    3dab5c7 View commit details
  4. feat: Angular support

    harlan-zw committed Jan 26, 2025
    Copy the full SHA
    671fdd5 View commit details

Commits on Jan 30, 2025

  1. chore: drop angular tests

    harlan-zw committed Jan 30, 2025
    Copy the full SHA
    c65f31a View commit details
  2. chore: drop angular tests

    harlan-zw committed Jan 30, 2025
    Copy the full SHA
    c8caa80 View commit details
  3. chore: bump deps and lint

    harlan-zw committed Jan 30, 2025
    Copy the full SHA
    399b927 View commit details
  4. docs: migrate docs for v2 (#460)

    * docs: v2
    
    * docs: v2
    
    * chore: bump
    
    * chore: bump Nuxt SEO 2.1
    
    * chore: docs progress
    
    * chore: progress commit
    
    * chore: progress commit
    
    * chore: sync lock
    
    * chore: missing dep
    harlan-zw authored Jan 30, 2025
    Copy the full SHA
    ee4a6e2 View commit details
Showing 1,006 changed files with 47,887 additions and 40,497 deletions.
155 changes: 155 additions & 0 deletions .claude/commands/analyze-docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
# Documentation Analysis and Improvement

This prompt helps analyze and improve Unhead documentation to create clean, approachable, and comprehensive documentation similar to anthropic.com, Vue.js, and Laravel.

## Documentation Analysis

When analyzing existing Unhead documentation, evaluate and provide recommendations for:

1. **Structure clarity**: Assess logical flow and hierarchy of information
2. **Completeness**: Identify missing concepts, edge cases, or examples
3. **Code examples**: Evaluate quality, relevance, and clarity
4. **Framework-specific guidance**: Check appropriate distinction between core and framework-specific features

## Documentation Standards

### Platform
- **Nuxt Content v3**: Documentation is built with Nuxt Content v3
- **MDC Format**: Content is authored in MDC (Markdown Components) format
- **Nuxt Components**: Leverage built-in and custom components. Use `::caution`, `::tip`, `::warning` and `::note` where appropriate

### MDC Syntax Guidelines
- **Component Usage**: Use `::component{}` syntax for components
- **Nested Components**: Structure complex components with proper nesting
- **Props**: Pass props to components using curly braces `{prop: value}`
- **Slots**: Use the slot syntax for component content
- **Code Blocks**: Use language-specific code blocks with syntax highlighting
- **Component Libraries**: Leverage built-in UI components from Nuxt Content

### Code Blocks
- **Language Tags**: All used hooks `useHead`, `useSeoMeta` should be imported from `@unhead/dynamic-import`
- **Import statements**: Include complete import statements
- **Framework-agnostic**: Unless explicitly in framework-specific section
- **Progressive complexity**: Simple examples first, then complex ones
- **Complete and minimal**: Include all necessary code, but no more
- **TypeScript**: Prefer TypeScript in examples when appropriate

### Structure
- **Consistent Hierarchy**: Organize with clear numbered sections (0.introduction.md, 1.guides/...)
- **Progressive Disclosure**: Basic concepts first, advanced topics later
- **Separation of Concerns**: Keep guides, API references, and examples distinct

### Content Guidelines
- **Clear and concise**: Use direct, simple language with minimal jargon
- **Conversational but professional**: Write as if explaining to a colleague
- **Active voice**: Prefer "Configure the plugin by..." over "The plugin can be configured by..."
- **Practical Examples**: Every feature should have real-world code examples
- **Complete API References**: Document all parameters, return values, and types
- **Starter Recipes**: Provide copy-paste solutions for common use cases
- **Troubleshooting**: Include common issues and their solutions
- **Framework Specifics**: Unless explicitly stated, examples should be framework-agnostic and code examples should import Unhead composables from `@unhead/dynamic-import`
- **Common Use Cases**: Include a "Common Use Cases" section where applicable to demonstrate practical applications
- **Best Practices**: Add guidance on recommended approaches and patterns
- **API Caveats**: Clearly document any limitations, edge cases, or unexpected behaviors
- **Internal Linking**: Add links to related sections within the documentation for easy navigation. We can find all links in the `src/content/docs/` folder as paths will map to the URL structure (without number prefixes)
- **Deprecation Functions**: Any of the `useServer*` composables are deprecated, we should recommend using `useHead` instead with `import.meta.server` instead and link to docs/1.guides/7.client-only-tags.md

### Page Structure
- **Front Matter**: Include YAML front matter with title, description and navigation.title. These should be optimized for SEO without keyword stuffing, reference specific frameworks if applicable
- **Title**: Not needed, this is generated from the front matter
- **Introduction**: Brief overview of the concept/component with high-level navigation to major sections
- **Core Concepts**: Breakdown of main functionality with clear section headings
- **Usage Examples**: Code examples with explanations
- **Advanced Usage**: More complex scenarios and advanced features when applicable
- **API Reference**: When applicable, with props/events/slots
- **Related Resources**: Links to relevant documentation to create a connected knowledge base

### Formatting
- **Alert Components**: Use specific alert components based on context:
- `::tip` - For best practices and recommendations
- `::note` - For additional information or context
- `::warning` or `::caution` - For potential issues or gotchas
- `::alert{type="info"}` - For general important notes
- **Tabs**: Use tab components for multi-framework examples
- **Code Groups**: Use code groups for related code examples
- **Diagrams**: Use visual aids for complex concepts
- **Consistency**: Maintain consistent terminology throughout
- **Inline Code**: When rendering inline `code` tags, always postfix with the lang:
- HTML tags: `<head>`{lang="html"}
- CSS/properties: `color`{lang="css"}
- JS/TS variables and code: `tagPriority`{lang="bash"} or `function`{lang="ts"}
- **Section Links**: Use anchor links to create navigation between sections of the document

### Maintenance
- **Version Specificity**: Indicate which version features were introduced
- **Regular Reviews**: Schedule periodic reviews to ensure accuracy
- **Deprecation Notices**: Clearly mark deprecated features
- **Change Log**: Maintain detailed documentation changes

## Section Templates

### Marketing Section

Some sections are more marketing-oriented, such as the introduction and overview. These should be written in a more engaging and less technical tone.
They should use specific components to enhance the user experience.

```md
::UPageCard{title="Tailwind CSS" description="Nuxt UI v3 integrates with latest Tailwind CSS v4, bringing significant improvements." icon="i-simple-icons-tailwindcss" orientation="horizontal" spotlight spotlight-color="primary"}
:img{src="/tailwindcss-v4.svg" alt="Tailwind CSS" class="w-full"}
::
```

### API Reference Section

```md
## API Reference

### Parameters

| Name | Type | Default | Description |
|------|------|---------|-------------|
| `param1` | `string` | `'default'` | Description of parameter |

### Returns

Description of return value and type

### Examples

```ts
// Simple example
```

### Component Section

```md
## ComponentName

Description of the component and when to use it.

### Props

| Name | Type | Default | Description |
|------|------|---------|-------------|
| `prop1` | `string` | `'default'` | Description of prop |

### Example

```vue
<template>
<ComponentName prop1="value" />
</template>
```

## Improvement Checklist

- [ ] Ensure all headings and page titles are clear and descriptive
- [ ] Confirm all code examples are complete and working
- [ ] Check that all parameters and options are documented
- [ ] Verify internal links are working correctly
- [ ] Review for consistent terminology
- [ ] Add practical examples for common use cases
- [ ] Include troubleshooting sections for common issues
- [ ] Provide migration paths from previous versions or other libraries

Unhead is a framework-agnostic head management library for managing HTML head tags in both client and server environments.
157 changes: 157 additions & 0 deletions .claude/commands/security-research.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Unhead Security Research Report

## Executive Summary

This report documents an assessment of Unhead's security measures against Cross-Site Scripting (XSS) vulnerabilities. Unhead is a framework-agnostic head management library for managing HTML head tags, making it a critical component in the security posture of web applications that use it.

## Methodology

- Code analysis of XSS prevention mechanisms
- Review of existing XSS test coverage
- Identification of potential attack vectors

## Findings

### XSS Protection Mechanisms

Unhead implements several security measures to prevent XSS:

1. **HTML Escaping**: The library uses the `escapeHtml` function in `tagToString.ts` which properly escapes the following characters:
- `&` β†’ `&amp;`
- `<` β†’ `&lt;`
- `>` β†’ `&gt;`
- `"` β†’ `&quot;`
- `'` β†’ `&#x27;`
- `/` β†’ `&#x2F;`

2. **Attribute Encoding**: The `encodeAttribute` function in `propsToString.ts` escapes double quotes in attribute values using `&quot;`.

3. **Content Type Handling**: Different encoding is applied based on content type:
- `textContent` is properly escaped to prevent XSS
- `innerHTML` is intentionally not escaped (dangerous but documented)

### Potential Vulnerabilities

1. **innerHTML Handling**: The code explicitly notes that innerHTML is "dangerously" used without encoding. This is a deliberate design choice to allow HTML in certain contexts, but creates a potential attack vector if user input is directly passed to innerHTML.

2. **Script Content**: When using script tags, the code properly handles the potential injection of `</script>` tags by using the `devalue` library, which correctly escapes such content.

3. **Attribute Escaping**: While double quotes are escaped in attribute values, the encoding is limited compared to the more comprehensive `escapeHtml` function. This might present edge cases in certain browser contexts.

4. **JSON Content**: Special attention is given to JSON content in script tags, with proper escaping via the `devalue` library.

## Attack Vectors to Explore

1. **Contextual Bypasses**: Test if escaping works in all contexts (HTML, attributes, JavaScript, CSS, URL)
2. **DOM-based XSS**: Examine client-side rendering for potential DOM XSS vectors
3. **Template Injection**: Test handling of complex template variables and expressions
4. **Script Execution**: Test script loading mechanisms and event handling
5. **Sanitization Bypass**: Attempt to bypass the escaping mechanisms

## Recommendations

1. **innerHTML Usage**: Consider adding a sanitization option for innerHTML content
2. **Attribute Encoding**: Enhance the attribute encoding to match the more comprehensive HTML encoding
3. **Additional Test Coverage**: Expand test coverage for edge cases and exotic payloads
4. **User Documentation**: Clearly document security best practices for users of the library
5. **URL Sanitization**: Implement sanitization for potentially dangerous URL schemas like javascript:, data:, and vbscript:
6. **Meta Tag Content**: Add proper content sanitization for meta tags to prevent SVG-based and other XSS payloads
7. **CSS Protection**: Consider sanitizing style content to prevent CSS-based attacks

## Comprehensive XSS Example

```js
// Example of a useHead() call that attempts to exploit multiple XSS vectors
useHead({
// Title injection
title: '</title><script>alert("title XSS")</script>',
titleTemplate: '%s - <script>alert("template")</script>',

// Meta tag vectors
meta: [
// SVG-based XSS
{ name: 'description', content: '<svg><script>alert("svg")</script></svg>' },
// Attribute injection
{ name: 'keywords" onload="alert("attr")', content: 'SEO keywords' },
// Unicode escape sequence
{ name: 'author', content: '\\u003Cscript\\u003Ealert("unicode")\\u003C/script\\u003E' },
// Character encoding attack
{ 'http-equiv': 'content-type', content: 'text/html; charset=UTF-7; X-XSS-Protection: "0";' },
// Case variation bypass
{ name: 'viewport', content: '<ScRiPt>alert("case")</ScRiPt>' },
// Emoji obfuscation
{ name: 'robots', content: 'πŸ“βž‘οΈ<script>alert("emoji")</script>' },
// innerHTML attempt (shouldn't work)
{ name: 'generator', innerHTML: '<script>alert("meta-inner")</script>' }
],

// Link attacks
link: [
// Javascript protocol
{ rel: 'stylesheet', href: 'javascript:alert("js-protocol")' },
// Data URI
{ rel: 'icon', href: 'data:text/html;base64,PHNjcmlwdD5hbGVydCgieHNzIik8L3NjcmlwdD4=' },
// Protocol switching
{ rel: 'dns-prefetch', href: '//evil.com/xss.js' },
// HTML-encoded colon
{ rel: 'preconnect', href: 'javascript&#58;alert("encoded")' },
// Other protocol
{ rel: 'prefetch', href: 'vbscript:alert("vbscript")' }
],

// Script attacks
script: [
// Template literal with closing script
{ innerHTML: `
const template = \`
</script>
<img src=x onerror="alert('template-literal')">
<script>
\`;
console.log(template);
` },
// Null byte insertion
{ innerHTML: `console.log("Null byte attack: \\0')</script><script>alert('null-byte')</script>")` },
// String concatenation bypass
{ innerHTML: `console.log("</scr"+"ipt><script>alert('concat')</script>")` },
// mXSS payload
{ innerHTML: `var xss = '<img src="1" onerror="alert(\\'mxss\\')" />';` },
// JSON with devalue (should be properly escaped)
{ type: 'application/json', innerHTML: JSON.stringify({ payload: '</script><script>alert("json")</script>' }) }
],

// Style attacks
style: [
// CSS expression
{ innerHTML: `body { color: expression(alert('css-expression')) }` },
// CSS url injection
{ innerHTML: `body { background: url('javascript:alert("css-url")') }` }
],

// HTML attributes
htmlAttrs: {
'onload': 'alert("html-event")',
'data-attr': '"><script>alert("html-attr")</script>'
},

// Body attributes
bodyAttrs: {
'data-custom': `x" onmouseover="alert('body-attr')" data-x="`
},

// Base attack
base: { href: 'javascript:alert("base")' }
})
```

## Next Steps

Further testing required:
- Dynamic content insertion
- Framework-specific integration points
- Client-side rendering security
- Event handler sanitization

---

*This is an initial security assessment and should be followed by comprehensive penetration testing.*
23 changes: 23 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!---
☝️ PR title should follow conventional commits (https://conventionalcommits.org)
-->

### πŸ”— Linked issue

<!-- If it resolves an open issue, please link the issue here. For example "Resolves #123" -->

### ❓ Type of change

<!-- What types of changes does your code introduce? Put an `x` in all the boxes that apply. -->

- [ ] πŸ“– Documentation (updates to the documentation or readme)
- [ ] 🐞 Bug fix (a non-breaking change that fixes an issue)
- [ ] πŸ‘Œ Enhancement (improving an existing functionality)
- [ ] ✨ New feature (a non-breaking change that adds functionality)
- [ ] 🧹 Chore (updates to the build process or auxiliary tools and libraries)
- [ ] ⚠️ Breaking change (fix or feature that would cause existing functionality to change)

### πŸ“š Description

<!-- Describe your changes in detail -->
<!-- Why is this change required? What problem does it solve? -->
52 changes: 52 additions & 0 deletions .github/workflows/bundle-size.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Bundle Size Analysis

on:
pull_request:
branches:
- main

permissions:
pull-requests: write

jobs:
analyze-bundle-size:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Install pnpm
uses: pnpm/action-setup@v4.1.0

- name: Use Node.js 22.x
uses: actions/setup-node@v4
with:
node-version: 22.x
registry-url: https://registry.npmjs.org/
cache: pnpm

- name: Install dependencies
run: pnpm install

- name: Run bundle size test
run: pnpm test:bundle-size

- uses: oven-sh/setup-bun@v2

- name: Run analyze script
id: analyze
run: |
output=$(bun bench/bundle/analyze.ts)
echo "$output"
echo "result<<EOF" >> $GITHUB_OUTPUT
echo "$output" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Post comment on PR
uses: marocchino/sticky-pull-request-comment@v2
with:
header: Bundle Size Analysis
message: |
### Bundle Size Analysis
${{ steps.analyze.outputs.result }}
Loading