@cosmicdrift/kumiko-bundled-features 0.48.1 → 0.51.0
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/package.json +9 -6
- package/src/auth-email-password/__tests__/signup-flow.integration.test.ts +51 -0
- package/src/auth-email-password/constants.ts +6 -0
- package/src/auth-email-password/errors.ts +19 -0
- package/src/auth-email-password/handlers/request-email-verification.write.ts +1 -0
- package/src/auth-email-password/handlers/request-password-reset.write.ts +1 -0
- package/src/auth-email-password/handlers/signup-confirm.write.ts +22 -12
- package/src/auth-email-password/handlers/signup-request.write.ts +12 -10
- package/src/auth-email-password/i18n.ts +4 -0
- package/src/auth-email-password/password-hashing.ts +1 -0
- package/src/auth-email-password/reset-token.ts +2 -0
- package/src/auth-email-password/seeding.ts +19 -4
- package/src/auth-email-password/signup-token-store.ts +1 -0
- package/src/auth-email-password/verification-token.ts +2 -0
- package/src/billing-foundation/aggregate-id.ts +1 -0
- package/src/cap-counter/aggregate-id.ts +2 -0
- package/src/config/__tests__/app-override-visibility.integration.test.ts +143 -0
- package/src/config/__tests__/backing-secrets.integration.test.ts +188 -0
- package/src/config/__tests__/cascade.integration.test.ts +111 -1
- package/src/config/__tests__/config.integration.test.ts +60 -0
- package/src/config/__tests__/env-overrides.test.ts +134 -0
- package/src/config/__tests__/inherited-redaction.integration.test.ts +180 -0
- package/src/config/__tests__/read-redaction.test.ts +112 -0
- package/src/config/__tests__/settings-hub-feature-name.test.ts +14 -0
- package/src/config/constants.ts +3 -1
- package/src/config/feature.ts +5 -2
- package/src/config/handlers/cascade.query.ts +13 -2
- package/src/config/handlers/readiness.query.ts +1 -0
- package/src/config/handlers/reset.write.ts +23 -2
- package/src/config/handlers/set.write.ts +36 -2
- package/src/config/handlers/values.query.ts +39 -13
- package/src/config/index.ts +1 -1
- package/src/config/read-redaction.ts +54 -0
- package/src/config/resolver.ts +163 -4
- package/src/config/web/client-plugin.ts +24 -0
- package/src/config/web/i18n.ts +25 -0
- package/src/config/web/index.ts +3 -0
- package/src/config/write-helpers.ts +37 -0
- package/src/custom-fields/aggregate-id.ts +1 -0
- package/src/custom-fields/wire-for-entity.ts +1 -0
- package/src/delivery/upsert-preference.ts +1 -0
- package/src/file-provider-inmemory/feature.ts +1 -1
- package/src/file-provider-s3/feature.ts +1 -1
- package/src/jobs/__tests__/projection-rebuild-job.integration.test.ts +162 -0
- package/src/jobs/feature.ts +13 -0
- package/src/jobs/handlers/projection-rebuild.job.ts +36 -0
- package/src/legal-pages/README.md +16 -13
- package/src/legal-pages/__tests__/legal-pages.integration.test.ts +15 -8
- package/src/legal-pages/feature.ts +9 -4
- package/src/legal-pages/markdown.ts +6 -56
- package/src/legal-pages/security-headers.ts +1 -0
- package/src/mail-transport-inmemory/feature.ts +1 -1
- package/src/mail-transport-smtp/feature.ts +1 -1
- package/src/managed-pages/__tests__/managed-pages.integration.test.ts +536 -0
- package/src/managed-pages/branding.ts +142 -0
- package/src/managed-pages/css-gate.ts +24 -0
- package/src/managed-pages/feature.ts +246 -0
- package/src/managed-pages/handlers/branding.query.ts +30 -0
- package/src/managed-pages/handlers/by-slug.query.ts +35 -0
- package/src/managed-pages/handlers/set.write.ts +113 -0
- package/src/managed-pages/index.ts +30 -0
- package/src/managed-pages/screens/branding-screen.ts +85 -0
- package/src/managed-pages/screens/page-screens.ts +82 -0
- package/src/managed-pages/seeding.ts +99 -0
- package/src/managed-pages/table.ts +58 -0
- package/src/page-render/__tests__/branding.test.ts +57 -0
- package/src/page-render/__tests__/css-sanitize.test.ts +215 -0
- package/src/page-render/__tests__/markdown.test.ts +41 -0
- package/src/page-render/branding.ts +99 -0
- package/src/page-render/css-sanitize.ts +344 -0
- package/src/page-render/index.ts +13 -0
- package/src/page-render/layout.ts +100 -0
- package/src/page-render/markdown.ts +39 -0
- package/src/page-render/security-headers.ts +16 -0
- package/src/step-dispatcher/mail-runner.ts +1 -0
- package/src/subscription-stripe/runtime.ts +1 -0
- package/src/subscription-stripe/verify-webhook.ts +1 -0
- package/src/tenant/__tests__/multi-tenant.integration.test.ts +48 -0
- package/src/tenant/handlers/list.query.ts +1 -1
- package/src/tenant/handlers/memberships.query.ts +16 -15
- package/src/tenant/handlers/toggle-enabled.write.ts +1 -1
- package/src/tenant/handlers/update.write.ts +1 -1
- package/src/text-content/api.ts +1 -0
- package/src/tier-engine/aggregate-id.ts +1 -0
- package/src/user/handlers/me.query.ts +1 -1
- package/src/user-data-rights/db/queries/export-jobs.ts +1 -0
- package/src/user-data-rights/deletion-token.ts +2 -0
- package/src/user-data-rights/feature.ts +1 -1
- package/src/user-data-rights/run-export-jobs.ts +2 -0
- package/src/user-profile/handlers/change-email.write.ts +1 -0
|
@@ -141,12 +141,9 @@ describe("legal-pages :: edge-cases", () => {
|
|
|
141
141
|
expect(body).toContain("Tenant-Admin");
|
|
142
142
|
});
|
|
143
143
|
|
|
144
|
-
test("Markdown-Body mit <script> wird
|
|
145
|
-
//
|
|
146
|
-
//
|
|
147
|
-
// ('XSS — bewusst aktuell nicht gesichert'). Test pinnt das
|
|
148
|
-
// Verhalten — wenn es sich ändert (z.B. DOMPurify dazu), schlägt
|
|
149
|
-
// dieser Test fehl und der Wechsel ist dokumentiert.
|
|
144
|
+
test("Markdown-Body mit <script> wird escaped (XSS-Härtung)", async () => {
|
|
145
|
+
// Server-Render ist gegen untrusted Tenant-Authoren gehärtet:
|
|
146
|
+
// Raw-HTML im Markdown-Body wird als Text escaped (kein Passthrough).
|
|
150
147
|
await seedTextBlock(db, {
|
|
151
148
|
tenantId: SYSTEM_TENANT_ID,
|
|
152
149
|
slug: "imprint",
|
|
@@ -158,8 +155,8 @@ describe("legal-pages :: edge-cases", () => {
|
|
|
158
155
|
const res = await stack.app.request("/legal/impressum");
|
|
159
156
|
expect(res.status).toBe(200);
|
|
160
157
|
const html = await res.text();
|
|
161
|
-
|
|
162
|
-
expect(html).toContain("
|
|
158
|
+
expect(html).not.toContain("<script>window.x=1</script>");
|
|
159
|
+
expect(html).toContain("<script>");
|
|
163
160
|
});
|
|
164
161
|
});
|
|
165
162
|
|
|
@@ -170,6 +167,16 @@ describe("legal-pages :: cache-control", () => {
|
|
|
170
167
|
});
|
|
171
168
|
});
|
|
172
169
|
|
|
170
|
+
describe("legal-pages :: security headers", () => {
|
|
171
|
+
test("server-gerenderte Pages tragen CSP + Hardening-Header", async () => {
|
|
172
|
+
const res = await stack.app.request("/legal/impressum");
|
|
173
|
+
expect(res.headers.get("content-security-policy")).toContain("script-src 'none'");
|
|
174
|
+
expect(res.headers.get("x-content-type-options")).toBe("nosniff");
|
|
175
|
+
expect(res.headers.get("x-frame-options")).toBe("SAMEORIGIN");
|
|
176
|
+
expect(res.headers.get("referrer-policy")).toBe("strict-origin-when-cross-origin");
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
173
180
|
describe("markdown render helpers", () => {
|
|
174
181
|
test("renderMarkdownToHtml converts markdown to HTML", () => {
|
|
175
182
|
const html = renderMarkdownToHtml("# Title\n\n**bold**");
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from "@cosmicdrift/kumiko-framework/engine";
|
|
10
10
|
import { LEGAL_REQUIRED_BLOCKS, LEGAL_ROUTES } from "./constants";
|
|
11
11
|
import { renderMarkdownToHtml, wrapInLayout } from "./markdown";
|
|
12
|
+
import { securePageHeaders } from "./security-headers";
|
|
12
13
|
|
|
13
14
|
// QN-Konstante als dokumentierter Public-Contract des text-content-
|
|
14
15
|
// Features. Ein magic-string statt eines Code-Imports ist hier explizit
|
|
@@ -113,10 +114,14 @@ export function createLegalPagesFeature(opts: LegalPagesOptions = {}): FeatureDe
|
|
|
113
114
|
lang: route.lang,
|
|
114
115
|
});
|
|
115
116
|
|
|
116
|
-
return c.body(
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
return c.body(
|
|
118
|
+
html,
|
|
119
|
+
200,
|
|
120
|
+
securePageHeaders({
|
|
121
|
+
"content-type": "text/html; charset=utf-8",
|
|
122
|
+
"cache-control": "public, max-age=300",
|
|
123
|
+
}),
|
|
124
|
+
);
|
|
120
125
|
},
|
|
121
126
|
});
|
|
122
127
|
}
|
|
@@ -1,57 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// Re-export aus dem geteilten page-render-Kern (managed-pages nutzt
|
|
2
|
+
// denselben gehärteten Renderer + Default-Layout). Namen bleiben stabil
|
|
3
|
+
// für legal-pages' Public-API (index.ts exportiert renderMarkdownToHtml +
|
|
4
|
+
// wrapInLayout).
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
// Task-Lists nicht nötig sind. Headers + Listen + Links + Code reichen.
|
|
7
|
-
//
|
|
8
|
-
// Instance statt globaler `marked.setOptions()` damit andere Features
|
|
9
|
-
// die `marked` als runtime-dep nutzen ihre eigenen Optionen behalten —
|
|
10
|
-
// modul-level side-effect auf shared library wäre brittle bei mehreren
|
|
11
|
-
// Konsumenten.
|
|
12
|
-
//
|
|
13
|
-
// XSS-Schutz: marked rendered tags 1:1, also kann ein böswilliger Text-
|
|
14
|
-
// Editor (TenantAdmin) <script>-Tags reinschreiben. Aktuell akzeptiert
|
|
15
|
-
// weil nur trusted Roles (TenantAdmin/SystemAdmin) Texte setzen können —
|
|
16
|
-
// bei einem Multi-Author-Setup müsste DOMPurify oder isomorphic-dompurify
|
|
17
|
-
// dazu. Dokumentiert in README, Phase-2-Hardening.
|
|
18
|
-
const markdownRenderer = new Marked({
|
|
19
|
-
gfm: false,
|
|
20
|
-
breaks: false,
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
export function renderMarkdownToHtml(markdown: string): string {
|
|
24
|
-
// @cast-boundary render-helper marked.parse return-type ist
|
|
25
|
-
// `string | Promise<string>` — mit `{ async: false }` runtime-garantiert
|
|
26
|
-
// sync (string). Cast nur API-Vertragsfix, kein Type-Loss.
|
|
27
|
-
return markdownRenderer.parse(markdown, { async: false }) as string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Layout-Wrapper für Legal-Pages — minimaler HTML-Skeleton mit Inline-
|
|
31
|
-
// CSS damit die Pages auch ohne App-Layout sauber aussehen. Apps die
|
|
32
|
-
// das in ihr eigenes Layout integrieren wollen, nutzen text-content's
|
|
33
|
-
// by-slug-query direkt und rendern selbst.
|
|
34
|
-
export function wrapInLayout(opts: { title: string; bodyHtml: string; lang: string }): string {
|
|
35
|
-
return `<!doctype html>
|
|
36
|
-
<html lang="${escapeHtmlAttr(opts.lang)}">
|
|
37
|
-
<head>
|
|
38
|
-
<meta charset="utf-8">
|
|
39
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
40
|
-
<title>${escapeHtml(opts.title)}</title>
|
|
41
|
-
<style>
|
|
42
|
-
body { font-family: system-ui, -apple-system, sans-serif; max-width: 720px;
|
|
43
|
-
margin: 2rem auto; padding: 0 1rem; line-height: 1.6; color: #222; }
|
|
44
|
-
h1, h2, h3 { line-height: 1.2; margin-top: 2rem; }
|
|
45
|
-
h1 { font-size: 1.8rem; } h2 { font-size: 1.4rem; } h3 { font-size: 1.15rem; }
|
|
46
|
-
a { color: #0066cc; }
|
|
47
|
-
code { background: #f4f4f4; padding: 0.1rem 0.3rem; border-radius: 3px; }
|
|
48
|
-
hr { border: 0; border-top: 1px solid #ddd; margin: 2rem 0; }
|
|
49
|
-
</style>
|
|
50
|
-
</head>
|
|
51
|
-
<body>
|
|
52
|
-
<main>
|
|
53
|
-
${opts.bodyHtml}
|
|
54
|
-
</main>
|
|
55
|
-
</body>
|
|
56
|
-
</html>`;
|
|
57
|
-
}
|
|
6
|
+
export { wrapInLayout } from "../page-render/layout";
|
|
7
|
+
export { renderSafeMarkdown as renderMarkdownToHtml } from "../page-render/markdown";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { securePageHeaders } from "../page-render/security-headers";
|
|
@@ -87,7 +87,7 @@ export const mailTransportInMemoryFeature = defineFeature(FEATURE_NAME, (r) => {
|
|
|
87
87
|
// Returnt den per-tenant Buffer. Identitätsstabil zwischen calls
|
|
88
88
|
// damit die Demo-Inbox accumulated bleibt.
|
|
89
89
|
return getOrCreateTransportForTenant(tenantId);
|
|
90
|
-
},
|
|
90
|
+
}, // @wrapper-known semantic-alias
|
|
91
91
|
};
|
|
92
92
|
r.useExtension("mailTransport", "inmemory", plugin);
|
|
93
93
|
});
|
|
@@ -110,7 +110,7 @@ export const mailTransportSmtpFeature = defineFeature(FEATURE_NAME, (r) => {
|
|
|
110
110
|
// `entityName` "smtp" is what tenants set in mail-foundation's
|
|
111
111
|
// `provider` config-key to pick this transport.
|
|
112
112
|
const plugin: MailTransportPlugin = {
|
|
113
|
-
build: async (ctx: HandlerContext, tenantId: string) => buildSmtpTransport(ctx, tenantId),
|
|
113
|
+
build: async (ctx: HandlerContext, tenantId: string) => buildSmtpTransport(ctx, tenantId), // @wrapper-known semantic-alias
|
|
114
114
|
};
|
|
115
115
|
r.useExtension("mailTransport", "smtp", plugin);
|
|
116
116
|
|