@tuturuuu/utils 0.15.0 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/package.json +3 -3
- package/src/next-config.test.ts +92 -0
- package/src/next-config.ts +23 -0
- package/src/platform-release.test.ts +1 -1
- package/src/platform-release.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.15.1](https://github.com/tutur3u/platform/compare/utils-v0.15.0...utils-v0.15.1) (2026-07-13)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **contacts:** restore group post mutations ([fe621e4](https://github.com/tutur3u/platform/commit/fe621e4d7e4126f705082cc3e815aafdeebe535a))
|
|
9
|
+
* **security:** prevent clickjacking across apps ([c8ed36e](https://github.com/tutur3u/platform/commit/c8ed36e708c08ebe3b17ee7f6249513644108c48))
|
|
10
|
+
|
|
3
11
|
## [0.15.0](https://github.com/tutur3u/platform/compare/utils-v0.14.0...utils-v0.15.0) (2026-07-11)
|
|
4
12
|
|
|
5
13
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tuturuuu/utils",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "0.15.
|
|
4
|
+
"version": "0.15.1",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "https://github.com/tutur3u/platform",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@tiptap/react": "3.27.3",
|
|
25
25
|
"@tuturuuu/google": "0.0.3",
|
|
26
26
|
"@tuturuuu/icons": "0.0.6",
|
|
27
|
-
"@tuturuuu/internal-api": "0.
|
|
27
|
+
"@tuturuuu/internal-api": "0.18.0",
|
|
28
28
|
"@tuturuuu/supabase": "0.4.0",
|
|
29
29
|
"@upstash/ratelimit": "^2.0.8",
|
|
30
30
|
"@upstash/redis": "^1.38.0",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"zod": "^4.4.3"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@tuturuuu/types": "0.
|
|
48
|
+
"@tuturuuu/types": "0.17.0",
|
|
49
49
|
"@tuturuuu/typescript-config": "0.1.1",
|
|
50
50
|
"@types/diff": "^8.0.0",
|
|
51
51
|
"@types/node": "^26.1.1",
|
package/src/next-config.test.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { tryToParsePath } from 'next/dist/lib/try-to-parse-path';
|
|
1
2
|
import { describe, expect, it } from 'vitest';
|
|
2
3
|
import {
|
|
3
4
|
createTuturuuuNextConfig,
|
|
@@ -40,6 +41,97 @@ describe('createTuturuuuNextConfig', () => {
|
|
|
40
41
|
});
|
|
41
42
|
});
|
|
42
43
|
|
|
44
|
+
it('protects responses from framing by default', async () => {
|
|
45
|
+
const config = createTuturuuuNextConfig();
|
|
46
|
+
|
|
47
|
+
await expect(config.headers?.()).resolves.toEqual([
|
|
48
|
+
{
|
|
49
|
+
source:
|
|
50
|
+
'/:path((?!api/v1/workspaces/[^/]+/external-projects/assets/[^/]+/webgl(?:/|$)).*)',
|
|
51
|
+
headers: [
|
|
52
|
+
{
|
|
53
|
+
key: 'Content-Security-Policy',
|
|
54
|
+
value: "frame-ancestors 'none'",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
key: 'X-Frame-Options',
|
|
58
|
+
value: 'DENY',
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
},
|
|
62
|
+
]);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('preserves app-specific response headers after shared security headers', async () => {
|
|
66
|
+
const config = createTuturuuuNextConfig({
|
|
67
|
+
async headers() {
|
|
68
|
+
return [
|
|
69
|
+
{
|
|
70
|
+
source: '/login',
|
|
71
|
+
headers: [
|
|
72
|
+
{
|
|
73
|
+
key: 'Cache-Control',
|
|
74
|
+
value: 'public, max-age=0, must-revalidate',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
key: 'X-Robots-Tag',
|
|
78
|
+
value: 'noindex, nofollow',
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const headers = await config.headers?.();
|
|
87
|
+
|
|
88
|
+
expect(headers).toHaveLength(2);
|
|
89
|
+
expect(headers?.[1]).toEqual({
|
|
90
|
+
source: '/login',
|
|
91
|
+
headers: [
|
|
92
|
+
{
|
|
93
|
+
key: 'Cache-Control',
|
|
94
|
+
value: 'public, max-age=0, must-revalidate',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
key: 'X-Robots-Tag',
|
|
98
|
+
value: 'noindex, nofollow',
|
|
99
|
+
},
|
|
100
|
+
],
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('exempts only the sandboxed CMS WebGL asset route from anti-framing headers', async () => {
|
|
105
|
+
const config = createTuturuuuNextConfig();
|
|
106
|
+
const [antiFramingRule] = (await config.headers?.()) ?? [];
|
|
107
|
+
const parsedPath = tryToParsePath(antiFramingRule?.source ?? '');
|
|
108
|
+
|
|
109
|
+
expect(parsedPath.error).toBeUndefined();
|
|
110
|
+
expect(parsedPath.regexStr).toBeDefined();
|
|
111
|
+
|
|
112
|
+
const routePattern = new RegExp(parsedPath.regexStr ?? '');
|
|
113
|
+
|
|
114
|
+
expect(routePattern.test('/')).toBe(true);
|
|
115
|
+
expect(routePattern.test('/login')).toBe(true);
|
|
116
|
+
expect(routePattern.test('/en/workspace')).toBe(true);
|
|
117
|
+
expect(routePattern.test('/api/billing/workspace/invoice')).toBe(true);
|
|
118
|
+
expect(
|
|
119
|
+
routePattern.test(
|
|
120
|
+
'/api/v1/workspaces/workspace-id/external-projects/assets/asset-id/webgl/index.html'
|
|
121
|
+
)
|
|
122
|
+
).toBe(false);
|
|
123
|
+
expect(
|
|
124
|
+
routePattern.test(
|
|
125
|
+
'/api/v1/workspaces/workspace-id/external-projects/assets/asset-id/webgl'
|
|
126
|
+
)
|
|
127
|
+
).toBe(false);
|
|
128
|
+
expect(
|
|
129
|
+
routePattern.test(
|
|
130
|
+
'/api/v1/workspaces/workspace-id/external-projects/assets/asset-id/webgl-preview'
|
|
131
|
+
)
|
|
132
|
+
).toBe(true);
|
|
133
|
+
});
|
|
134
|
+
|
|
43
135
|
it('allows the exact worktree-prefixed Portless host for Next dev assets', () => {
|
|
44
136
|
const originalPortlessUrl = process.env.PORTLESS_URL;
|
|
45
137
|
|
package/src/next-config.ts
CHANGED
|
@@ -33,6 +33,20 @@ export const TUTURUUU_NEXT_IMAGE_REMOTE_PATTERNS = [
|
|
|
33
33
|
},
|
|
34
34
|
] satisfies NextImageRemotePattern[];
|
|
35
35
|
|
|
36
|
+
const TUTURUUU_ANTI_FRAMING_SOURCE =
|
|
37
|
+
'/:path((?!api/v1/workspaces/[^/]+/external-projects/assets/[^/]+/webgl(?:/|$)).*)';
|
|
38
|
+
|
|
39
|
+
const TUTURUUU_ANTI_FRAMING_HEADERS = [
|
|
40
|
+
{
|
|
41
|
+
key: 'Content-Security-Policy',
|
|
42
|
+
value: "frame-ancestors 'none'",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
key: 'X-Frame-Options',
|
|
46
|
+
value: 'DENY',
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
|
|
36
50
|
function mergeStringArrays(
|
|
37
51
|
first: readonly string[] | undefined,
|
|
38
52
|
second: readonly string[] | undefined
|
|
@@ -107,6 +121,15 @@ export function createTuturuuuNextConfig(config: NextConfig = {}): NextConfig {
|
|
|
107
121
|
experimentalConfig.optimizePackageImports
|
|
108
122
|
),
|
|
109
123
|
},
|
|
124
|
+
async headers() {
|
|
125
|
+
return [
|
|
126
|
+
{
|
|
127
|
+
source: TUTURUUU_ANTI_FRAMING_SOURCE,
|
|
128
|
+
headers: TUTURUUU_ANTI_FRAMING_HEADERS,
|
|
129
|
+
},
|
|
130
|
+
...((await config.headers?.()) ?? []),
|
|
131
|
+
];
|
|
132
|
+
},
|
|
110
133
|
};
|
|
111
134
|
}
|
|
112
135
|
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
|
|
8
8
|
describe('platform release metadata', () => {
|
|
9
9
|
it('uses the centralized shared browser app version', () => {
|
|
10
|
-
expect(TUTURUUU_PLATFORM_VERSION).toBe('0.
|
|
10
|
+
expect(TUTURUUU_PLATFORM_VERSION).toBe('0.22.0'); // x-release-please-version
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
it('preserves the source timestamp in builtAt and derives a short hash', () => {
|
package/src/platform-release.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PLATFORM_BUILD_METADATA } from './generated/platform-build-metadata';
|
|
2
2
|
|
|
3
|
-
export const TUTURUUU_PLATFORM_VERSION = '0.
|
|
3
|
+
export const TUTURUUU_PLATFORM_VERSION = '0.22.0'; // x-release-please-version
|
|
4
4
|
|
|
5
5
|
export type PlatformBuildMetadataInput = {
|
|
6
6
|
/** ISO timestamp of the source commit used for the build, not deployment time. */
|