firebase-hosting-mcp-server 1.0.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/README.md +191 -0
- package/dist/constants.d.ts +75 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +179 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +269 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/index.d.ts +246 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +238 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/services/firebase-cli.d.ts +20 -0
- package/dist/services/firebase-cli.d.ts.map +1 -0
- package/dist/services/firebase-cli.js +164 -0
- package/dist/services/firebase-cli.js.map +1 -0
- package/dist/services/license.d.ts +6 -0
- package/dist/services/license.d.ts.map +1 -0
- package/dist/services/license.js +111 -0
- package/dist/services/license.js.map +1 -0
- package/dist/tools/configure.d.ts +3 -0
- package/dist/tools/configure.d.ts.map +1 -0
- package/dist/tools/configure.js +102 -0
- package/dist/tools/configure.js.map +1 -0
- package/dist/tools/delete-channel.d.ts +3 -0
- package/dist/tools/delete-channel.d.ts.map +1 -0
- package/dist/tools/delete-channel.js +33 -0
- package/dist/tools/delete-channel.js.map +1 -0
- package/dist/tools/deploy.d.ts +3 -0
- package/dist/tools/deploy.d.ts.map +1 -0
- package/dist/tools/deploy.js +123 -0
- package/dist/tools/deploy.js.map +1 -0
- package/dist/tools/harden.d.ts +3 -0
- package/dist/tools/harden.d.ts.map +1 -0
- package/dist/tools/harden.js +181 -0
- package/dist/tools/harden.js.map +1 -0
- package/dist/tools/init.d.ts +3 -0
- package/dist/tools/init.d.ts.map +1 -0
- package/dist/tools/init.js +60 -0
- package/dist/tools/init.js.map +1 -0
- package/dist/tools/preview.d.ts +3 -0
- package/dist/tools/preview.d.ts.map +1 -0
- package/dist/tools/preview.js +85 -0
- package/dist/tools/preview.js.map +1 -0
- package/dist/tools/rollback.d.ts +3 -0
- package/dist/tools/rollback.d.ts.map +1 -0
- package/dist/tools/rollback.js +87 -0
- package/dist/tools/rollback.js.map +1 -0
- package/dist/tools/status.d.ts +3 -0
- package/dist/tools/status.d.ts.map +1 -0
- package/dist/tools/status.js +80 -0
- package/dist/tools/status.js.map +1 -0
- package/dist/types.d.ts +81 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +43 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
// firebase_hosting_configure - Modifies firebase.json hosting config
|
|
2
|
+
import { readFirebaseJson, writeFirebaseJson, } from "../services/firebase-cli.js";
|
|
3
|
+
export async function handleConfigure(params) {
|
|
4
|
+
const { project_dir } = params;
|
|
5
|
+
const config = await readFirebaseJson(project_dir);
|
|
6
|
+
if (!config) {
|
|
7
|
+
return `❌ No firebase.json found in ${project_dir}. Run firebase_hosting_init first.`;
|
|
8
|
+
}
|
|
9
|
+
const changes = [];
|
|
10
|
+
// Update public directory
|
|
11
|
+
if (params.public_dir !== undefined) {
|
|
12
|
+
config.hosting.public = params.public_dir;
|
|
13
|
+
changes.push(`Public directory → ${params.public_dir}`);
|
|
14
|
+
}
|
|
15
|
+
// Toggle SPA rewrites
|
|
16
|
+
if (params.spa !== undefined) {
|
|
17
|
+
if (params.spa) {
|
|
18
|
+
// Add SPA rewrite if not exists
|
|
19
|
+
const hasSpRewrite = config.hosting.rewrites?.some((r) => r.source === "**" && r.destination === "/index.html");
|
|
20
|
+
if (!hasSpRewrite) {
|
|
21
|
+
config.hosting.rewrites = config.hosting.rewrites || [];
|
|
22
|
+
config.hosting.rewrites.push({
|
|
23
|
+
source: "**",
|
|
24
|
+
destination: "/index.html",
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
changes.push("SPA rewrites → enabled");
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
config.hosting.rewrites = config.hosting.rewrites?.filter((r) => !(r.source === "**" && r.destination === "/index.html"));
|
|
31
|
+
changes.push("SPA rewrites → disabled");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Clean URLs
|
|
35
|
+
if (params.clean_urls !== undefined) {
|
|
36
|
+
config.hosting.cleanUrls = params.clean_urls;
|
|
37
|
+
changes.push(`Clean URLs → ${params.clean_urls}`);
|
|
38
|
+
}
|
|
39
|
+
// Trailing slash
|
|
40
|
+
if (params.trailing_slash !== undefined) {
|
|
41
|
+
config.hosting.trailingSlash = params.trailing_slash;
|
|
42
|
+
changes.push(`Trailing slash → ${params.trailing_slash}`);
|
|
43
|
+
}
|
|
44
|
+
// Add redirect
|
|
45
|
+
if (params.add_redirect) {
|
|
46
|
+
config.hosting.redirects = config.hosting.redirects || [];
|
|
47
|
+
config.hosting.redirects.push({
|
|
48
|
+
source: params.add_redirect.source,
|
|
49
|
+
destination: params.add_redirect.destination,
|
|
50
|
+
type: params.add_redirect.type,
|
|
51
|
+
});
|
|
52
|
+
changes.push(`Added redirect: ${params.add_redirect.source} → ${params.add_redirect.destination} (${params.add_redirect.type})`);
|
|
53
|
+
}
|
|
54
|
+
// Add header
|
|
55
|
+
if (params.add_header) {
|
|
56
|
+
config.hosting.headers = config.hosting.headers || [];
|
|
57
|
+
// Check if source pattern already has headers
|
|
58
|
+
const existing = config.hosting.headers.find((h) => h.source === params.add_header.source);
|
|
59
|
+
if (existing) {
|
|
60
|
+
existing.headers.push({
|
|
61
|
+
key: params.add_header.key,
|
|
62
|
+
value: params.add_header.value,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
config.hosting.headers.push({
|
|
67
|
+
source: params.add_header.source,
|
|
68
|
+
headers: [
|
|
69
|
+
{ key: params.add_header.key, value: params.add_header.value },
|
|
70
|
+
],
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
changes.push(`Added header: ${params.add_header.key} on ${params.add_header.source}`);
|
|
74
|
+
}
|
|
75
|
+
// Add rewrite
|
|
76
|
+
if (params.add_rewrite) {
|
|
77
|
+
config.hosting.rewrites = config.hosting.rewrites || [];
|
|
78
|
+
const rewrite = {
|
|
79
|
+
source: params.add_rewrite.source,
|
|
80
|
+
};
|
|
81
|
+
if (params.add_rewrite.destination) {
|
|
82
|
+
rewrite.destination = params.add_rewrite.destination;
|
|
83
|
+
}
|
|
84
|
+
if (params.add_rewrite.function) {
|
|
85
|
+
rewrite.function = params.add_rewrite.function;
|
|
86
|
+
}
|
|
87
|
+
config.hosting.rewrites.push(rewrite);
|
|
88
|
+
changes.push(`Added rewrite: ${params.add_rewrite.source}`);
|
|
89
|
+
}
|
|
90
|
+
if (changes.length === 0) {
|
|
91
|
+
return `⚠️ No changes specified. Provide at least one configuration option to update.`;
|
|
92
|
+
}
|
|
93
|
+
// Write updated config
|
|
94
|
+
await writeFirebaseJson(project_dir, config);
|
|
95
|
+
return [
|
|
96
|
+
`✅ firebase.json updated!`,
|
|
97
|
+
``,
|
|
98
|
+
`Changes:`,
|
|
99
|
+
...changes.map((c) => ` - ${c}`),
|
|
100
|
+
].join("\n");
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=configure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configure.js","sourceRoot":"","sources":["../../src/tools/configure.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete-channel.d.ts","sourceRoot":"","sources":["../../src/tools/delete-channel.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,MAAM,CAAC,CAgCjB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// firebase_hosting_delete_channel - Deletes a preview channel
|
|
2
|
+
import { runFirebaseCommand, checkFirebaseCli, checkFirebaseAuth, } from "../services/firebase-cli.js";
|
|
3
|
+
export async function handleDeleteChannel(params) {
|
|
4
|
+
const { project_dir, channel_id, project_id } = params;
|
|
5
|
+
const cliCheck = await checkFirebaseCli();
|
|
6
|
+
if (!cliCheck.installed)
|
|
7
|
+
return `❌ ${cliCheck.message}`;
|
|
8
|
+
const authCheck = await checkFirebaseAuth();
|
|
9
|
+
if (!authCheck.authenticated)
|
|
10
|
+
return `❌ ${authCheck.message}`;
|
|
11
|
+
const deleteArgs = [
|
|
12
|
+
"hosting:channel:delete",
|
|
13
|
+
channel_id,
|
|
14
|
+
"--force",
|
|
15
|
+
"--json",
|
|
16
|
+
];
|
|
17
|
+
if (project_id)
|
|
18
|
+
deleteArgs.push("--project", project_id);
|
|
19
|
+
const result = await runFirebaseCommand(deleteArgs, project_dir, 30_000);
|
|
20
|
+
if (!result.success) {
|
|
21
|
+
if (result.stderr.includes("NOT_FOUND") || result.stderr.includes("not found")) {
|
|
22
|
+
return `❌ Channel "${channel_id}" not found. Use firebase_hosting_status to see active channels.`;
|
|
23
|
+
}
|
|
24
|
+
return `❌ Failed to delete channel: ${result.stderr.slice(0, 500)}`;
|
|
25
|
+
}
|
|
26
|
+
return [
|
|
27
|
+
`✅ Preview channel deleted!`,
|
|
28
|
+
``,
|
|
29
|
+
`Channel: ${channel_id}`,
|
|
30
|
+
`The preview URL is no longer accessible.`,
|
|
31
|
+
].join("\n");
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=delete-channel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delete-channel.js","sourceRoot":"","sources":["../../src/tools/delete-channel.ts"],"names":[],"mappings":"AAAA,8DAA8D;AAC9D,OAAO,EACL,kBAAkB,EAClB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,6BAA6B,CAAC;AAGrC,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAA0B;IAE1B,MAAM,EAAE,WAAW,EAAE,UAAU,EAAE,UAAU,EAAE,GAAG,MAAM,CAAC;IAEvD,MAAM,QAAQ,GAAG,MAAM,gBAAgB,EAAE,CAAC;IAC1C,IAAI,CAAC,QAAQ,CAAC,SAAS;QAAE,OAAO,KAAK,QAAQ,CAAC,OAAO,EAAE,CAAC;IAExD,MAAM,SAAS,GAAG,MAAM,iBAAiB,EAAE,CAAC;IAC5C,IAAI,CAAC,SAAS,CAAC,aAAa;QAAE,OAAO,KAAK,SAAS,CAAC,OAAO,EAAE,CAAC;IAE9D,MAAM,UAAU,GAAG;QACjB,wBAAwB;QACxB,UAAU;QACV,SAAS;QACT,QAAQ;KACT,CAAC;IACF,IAAI,UAAU;QAAE,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;IAEzD,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,UAAU,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC;IAEzE,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACpB,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;YAC/E,OAAO,cAAc,UAAU,kEAAkE,CAAC;QACpG,CAAC;QACD,OAAO,+BAA+B,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;IACtE,CAAC;IAED,OAAO;QACL,4BAA4B;QAC5B,EAAE;QACF,YAAY,UAAU,EAAE;QACxB,0CAA0C;KAC3C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/tools/deploy.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,wBAAsB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAkIvE"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// firebase_hosting_deploy - All-in-one: init + build + deploy
|
|
2
|
+
// Vibe coders just say "Use firebase-hosting-mcp to deploy" and everything happens.
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { runFirebaseCommand, checkFirebaseCli, checkFirebaseAuth, readFirebaseJson, detectFramework, runBuild, fileExists, } from "../services/firebase-cli.js";
|
|
5
|
+
import { handleInit } from "./init.js";
|
|
6
|
+
export async function handleDeploy(params) {
|
|
7
|
+
const { project_dir, project_id, message, build, build_command } = params;
|
|
8
|
+
const steps = [];
|
|
9
|
+
// 1. Check prerequisites
|
|
10
|
+
const cliCheck = await checkFirebaseCli();
|
|
11
|
+
if (!cliCheck.installed) {
|
|
12
|
+
return `❌ ${cliCheck.message}`;
|
|
13
|
+
}
|
|
14
|
+
const authCheck = await checkFirebaseAuth();
|
|
15
|
+
if (!authCheck.authenticated) {
|
|
16
|
+
return `❌ ${authCheck.message}`;
|
|
17
|
+
}
|
|
18
|
+
// 2. Auto-init if no firebase.json exists
|
|
19
|
+
let config = await readFirebaseJson(project_dir);
|
|
20
|
+
if (!config) {
|
|
21
|
+
steps.push("🔧 No firebase.json found — auto-initializing...");
|
|
22
|
+
const initResult = await handleInit({
|
|
23
|
+
api_key: params.api_key,
|
|
24
|
+
project_dir,
|
|
25
|
+
project_id,
|
|
26
|
+
});
|
|
27
|
+
if (!initResult.startsWith("✅")) {
|
|
28
|
+
return initResult; // Init failed, return the error
|
|
29
|
+
}
|
|
30
|
+
steps.push("✅ Firebase Hosting initialized");
|
|
31
|
+
// Re-read the freshly created config
|
|
32
|
+
config = await readFirebaseJson(project_dir);
|
|
33
|
+
if (!config) {
|
|
34
|
+
return `❌ Failed to create firebase.json. Check directory permissions.`;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// 3. Build if requested
|
|
38
|
+
if (build) {
|
|
39
|
+
const pkgExists = await fileExists(join(project_dir, "package.json"));
|
|
40
|
+
if (pkgExists) {
|
|
41
|
+
let cmd = build_command;
|
|
42
|
+
if (!cmd) {
|
|
43
|
+
const detected = await detectFramework(project_dir);
|
|
44
|
+
cmd = detected?.buildCommand || "npm run build";
|
|
45
|
+
}
|
|
46
|
+
steps.push(`🔨 Building... (${cmd})`);
|
|
47
|
+
const buildResult = await runBuild(project_dir, cmd);
|
|
48
|
+
if (!buildResult.success) {
|
|
49
|
+
return [
|
|
50
|
+
...steps,
|
|
51
|
+
`❌ Build failed!`,
|
|
52
|
+
``,
|
|
53
|
+
`Error output:`,
|
|
54
|
+
buildResult.stderr.slice(0, 2000),
|
|
55
|
+
``,
|
|
56
|
+
`Fix the build errors and try again, or set build=false to skip.`,
|
|
57
|
+
].join("\n");
|
|
58
|
+
}
|
|
59
|
+
steps.push("✅ Build successful");
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// 4. Check public directory exists
|
|
63
|
+
const publicDir = join(project_dir, config.hosting.public);
|
|
64
|
+
if (!(await fileExists(publicDir))) {
|
|
65
|
+
return [
|
|
66
|
+
...steps,
|
|
67
|
+
`❌ Public directory not found: ${config.hosting.public}`,
|
|
68
|
+
`Expected path: ${publicDir}`,
|
|
69
|
+
``,
|
|
70
|
+
`Make sure your build output matches the public directory in firebase.json.`,
|
|
71
|
+
].join("\n");
|
|
72
|
+
}
|
|
73
|
+
// 5. Deploy
|
|
74
|
+
steps.push("🚀 Deploying to Firebase Hosting...");
|
|
75
|
+
const deployArgs = ["deploy", "--only", "hosting", "--json"];
|
|
76
|
+
deployArgs.push("--project", project_id);
|
|
77
|
+
if (message) {
|
|
78
|
+
deployArgs.push("--message", message);
|
|
79
|
+
}
|
|
80
|
+
const result = await runFirebaseCommand(deployArgs, project_dir, 180_000);
|
|
81
|
+
if (!result.success) {
|
|
82
|
+
return [
|
|
83
|
+
...steps,
|
|
84
|
+
`❌ Deploy failed!`,
|
|
85
|
+
``,
|
|
86
|
+
`Error:`,
|
|
87
|
+
result.stderr.slice(0, 2000),
|
|
88
|
+
``,
|
|
89
|
+
`Common fixes:`,
|
|
90
|
+
` - Check your Firebase project permissions`,
|
|
91
|
+
` - Verify the project ID "${project_id}" is correct`,
|
|
92
|
+
` - Run "firebase login" to re-authenticate`,
|
|
93
|
+
].join("\n");
|
|
94
|
+
}
|
|
95
|
+
// 6. Parse result for URL
|
|
96
|
+
let siteUrl = "";
|
|
97
|
+
try {
|
|
98
|
+
const jsonOutput = JSON.parse(result.stdout);
|
|
99
|
+
const hosting = jsonOutput?.result?.hosting;
|
|
100
|
+
if (typeof hosting === "string") {
|
|
101
|
+
siteUrl = hosting;
|
|
102
|
+
}
|
|
103
|
+
else if (hosting?.site) {
|
|
104
|
+
siteUrl = `https://${hosting.site}.web.app`;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
const urlMatch = result.stdout.match(/https:\/\/[a-z0-9-]+\.(?:web\.app|firebaseapp\.com)/);
|
|
109
|
+
if (urlMatch) {
|
|
110
|
+
siteUrl = urlMatch[0];
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// 7. Final report
|
|
114
|
+
const report = [...steps, ``, `✅ Deploy successful!`, ``];
|
|
115
|
+
report.push(`Project: ${project_id}`);
|
|
116
|
+
if (siteUrl)
|
|
117
|
+
report.push(`URL: ${siteUrl}`);
|
|
118
|
+
if (message)
|
|
119
|
+
report.push(`Message: ${message}`);
|
|
120
|
+
report.push(`Account: ${authCheck.accounts[0] || "unknown"}`);
|
|
121
|
+
return report.join("\n");
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=deploy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/tools/deploy.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harden.d.ts","sourceRoot":"","sources":["../../src/tools/harden.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAgFvD,wBAAsB,YAAY,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAoJvE"}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
// firebase_hosting_harden - Apply security headers preset to firebase.json
|
|
2
|
+
// One-shot hardening: CSP, HSTS, X-Frame-Options, Permissions-Policy, caching.
|
|
3
|
+
// Merges with existing headers — never overwrites user-defined rules.
|
|
4
|
+
import { readFirebaseJson, writeFirebaseJson, } from "../services/firebase-cli.js";
|
|
5
|
+
import { DEFAULT_CSP_DIRECTIVES, SECURITY_HEADERS_GLOBAL, CACHE_IMMUTABLE_PATTERN, CACHE_IMMUTABLE_HEADERS, CACHE_HTML_PATTERN, CACHE_HTML_HEADERS, } from "../constants.js";
|
|
6
|
+
// ─── CSP Builder ─────────────────────────────────────────────────────
|
|
7
|
+
function buildCspHeader(params, strict) {
|
|
8
|
+
// Deep copy base directives
|
|
9
|
+
const directives = {};
|
|
10
|
+
for (const [key, values] of Object.entries(DEFAULT_CSP_DIRECTIVES)) {
|
|
11
|
+
directives[key] = [...values];
|
|
12
|
+
}
|
|
13
|
+
// Merge user-provided origins
|
|
14
|
+
if (params.csp_connect_src) {
|
|
15
|
+
directives["connect-src"].push(...params.csp_connect_src);
|
|
16
|
+
}
|
|
17
|
+
if (params.csp_script_src) {
|
|
18
|
+
directives["script-src"].push(...params.csp_script_src);
|
|
19
|
+
}
|
|
20
|
+
if (params.csp_style_src) {
|
|
21
|
+
directives["style-src"].push(...params.csp_style_src);
|
|
22
|
+
}
|
|
23
|
+
if (params.csp_frame_src) {
|
|
24
|
+
directives["frame-src"] = ["'self'", ...params.csp_frame_src];
|
|
25
|
+
}
|
|
26
|
+
// Standard preset: relax style-src to allow inline (most frameworks need it)
|
|
27
|
+
// Strict preset: keep it tight, only user-specified origins
|
|
28
|
+
if (!strict) {
|
|
29
|
+
// Ensure unsafe-inline is present for standard mode
|
|
30
|
+
if (!directives["style-src"].includes("'unsafe-inline'")) {
|
|
31
|
+
directives["style-src"].push("'unsafe-inline'");
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Strict mode additions
|
|
35
|
+
if (strict) {
|
|
36
|
+
directives["upgrade-insecure-requests"] = [];
|
|
37
|
+
}
|
|
38
|
+
// Build CSP string
|
|
39
|
+
return Object.entries(directives)
|
|
40
|
+
.map(([key, values]) => {
|
|
41
|
+
if (values.length === 0)
|
|
42
|
+
return key;
|
|
43
|
+
return `${key} ${values.join(" ")}`;
|
|
44
|
+
})
|
|
45
|
+
.join("; ");
|
|
46
|
+
}
|
|
47
|
+
// ─── Header Merge Logic ──────────────────────────────────────────────
|
|
48
|
+
/**
|
|
49
|
+
* Merges new security headers with existing ones.
|
|
50
|
+
* Strategy: for a given source pattern, add headers that don't already exist.
|
|
51
|
+
* Never overwrites a header the user already set (they may have customized it).
|
|
52
|
+
*/
|
|
53
|
+
function mergeHeaders(existing, newRule) {
|
|
54
|
+
const result = [...existing];
|
|
55
|
+
const match = result.find((r) => r.source === newRule.source);
|
|
56
|
+
if (match) {
|
|
57
|
+
// Add only headers not already present (by key)
|
|
58
|
+
const existingKeys = new Set(match.headers.map((h) => h.key.toLowerCase()));
|
|
59
|
+
for (const header of newRule.headers) {
|
|
60
|
+
if (!existingKeys.has(header.key.toLowerCase())) {
|
|
61
|
+
match.headers.push(header);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
result.push(newRule);
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
// ─── Main Handler ────────────────────────────────────────────────────
|
|
71
|
+
export async function handleHarden(params) {
|
|
72
|
+
const { project_dir, preset, cache_immutable, dry_run } = params;
|
|
73
|
+
const strict = preset === "strict";
|
|
74
|
+
// 1. Read existing config
|
|
75
|
+
const config = await readFirebaseJson(project_dir);
|
|
76
|
+
if (!config) {
|
|
77
|
+
return `❌ No firebase.json found in ${project_dir}. Run firebase_hosting_deploy or firebase_hosting_init first.`;
|
|
78
|
+
}
|
|
79
|
+
const applied = [];
|
|
80
|
+
const skipped = [];
|
|
81
|
+
let headers = config.hosting.headers || [];
|
|
82
|
+
// 2. Build CSP
|
|
83
|
+
const cspValue = buildCspHeader(params, strict);
|
|
84
|
+
// 3. Assemble security headers for **
|
|
85
|
+
const globalHeaders = [
|
|
86
|
+
...SECURITY_HEADERS_GLOBAL,
|
|
87
|
+
{ key: "Content-Security-Policy", value: cspValue },
|
|
88
|
+
];
|
|
89
|
+
// Strict mode: remove X-XSS-Protection (CSP makes it redundant and
|
|
90
|
+
// it can cause issues in modern browsers)
|
|
91
|
+
// Standard mode: keep it for legacy browser compat
|
|
92
|
+
if (!strict) {
|
|
93
|
+
globalHeaders.push({ key: "X-XSS-Protection", value: "1; mode=block" });
|
|
94
|
+
}
|
|
95
|
+
// 4. Merge global security headers
|
|
96
|
+
const globalRule = {
|
|
97
|
+
source: "**",
|
|
98
|
+
headers: globalHeaders,
|
|
99
|
+
};
|
|
100
|
+
const existingGlobal = headers.find((r) => r.source === "**");
|
|
101
|
+
const existingGlobalKeys = new Set((existingGlobal?.headers || []).map((h) => h.key.toLowerCase()));
|
|
102
|
+
// Track what's new vs already present
|
|
103
|
+
for (const h of globalHeaders) {
|
|
104
|
+
if (existingGlobalKeys.has(h.key.toLowerCase())) {
|
|
105
|
+
skipped.push(`${h.key} (already set)`);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
applied.push(`${h.key}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
headers = mergeHeaders(headers, globalRule);
|
|
112
|
+
// 5. Cache headers for static assets
|
|
113
|
+
if (cache_immutable) {
|
|
114
|
+
const cacheRule = {
|
|
115
|
+
source: CACHE_IMMUTABLE_PATTERN,
|
|
116
|
+
headers: CACHE_IMMUTABLE_HEADERS,
|
|
117
|
+
};
|
|
118
|
+
const htmlCacheRule = {
|
|
119
|
+
source: CACHE_HTML_PATTERN,
|
|
120
|
+
headers: CACHE_HTML_HEADERS,
|
|
121
|
+
};
|
|
122
|
+
const existingCache = headers.find((r) => r.source === CACHE_IMMUTABLE_PATTERN);
|
|
123
|
+
if (existingCache) {
|
|
124
|
+
skipped.push("Cache-Control on static assets (already set)");
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
headers = mergeHeaders(headers, cacheRule);
|
|
128
|
+
applied.push("Cache-Control: immutable on static assets");
|
|
129
|
+
}
|
|
130
|
+
const existingHtmlCache = headers.find((r) => r.source === CACHE_HTML_PATTERN);
|
|
131
|
+
if (existingHtmlCache) {
|
|
132
|
+
skipped.push("Cache-Control on HTML (already set)");
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
headers = mergeHeaders(headers, htmlCacheRule);
|
|
136
|
+
applied.push("Cache-Control: no-cache on HTML");
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
// 6. Dry run — preview without writing
|
|
140
|
+
if (dry_run) {
|
|
141
|
+
const report = [
|
|
142
|
+
`🔍 Dry run — no changes written`,
|
|
143
|
+
``,
|
|
144
|
+
`Preset: ${preset}`,
|
|
145
|
+
``,
|
|
146
|
+
];
|
|
147
|
+
if (applied.length > 0) {
|
|
148
|
+
report.push(`Would apply:`);
|
|
149
|
+
report.push(...applied.map((a) => ` ✅ ${a}`));
|
|
150
|
+
}
|
|
151
|
+
if (skipped.length > 0) {
|
|
152
|
+
report.push(``, `Already present (no change):`);
|
|
153
|
+
report.push(...skipped.map((s) => ` ⏭️ ${s}`));
|
|
154
|
+
}
|
|
155
|
+
report.push(``, `CSP directive:`, ` ${cspValue}`);
|
|
156
|
+
return report.join("\n");
|
|
157
|
+
}
|
|
158
|
+
// 7. Write updated config
|
|
159
|
+
config.hosting.headers = headers;
|
|
160
|
+
await writeFirebaseJson(project_dir, config);
|
|
161
|
+
// 8. Build report
|
|
162
|
+
const report = [
|
|
163
|
+
`✅ Security headers applied! (${preset} preset)`,
|
|
164
|
+
``,
|
|
165
|
+
];
|
|
166
|
+
if (applied.length > 0) {
|
|
167
|
+
report.push(`Applied:`);
|
|
168
|
+
report.push(...applied.map((a) => ` ✅ ${a}`));
|
|
169
|
+
}
|
|
170
|
+
if (skipped.length > 0) {
|
|
171
|
+
report.push(``, `Already present (kept your values):`);
|
|
172
|
+
report.push(...skipped.map((s) => ` ⏭️ ${s}`));
|
|
173
|
+
}
|
|
174
|
+
report.push(``, `CSP directive:`, ` ${cspValue}`);
|
|
175
|
+
report.push(``, `⚠️ Next steps:`, ` 1. Review the CSP — if your app loads external scripts/fonts/APIs, add their origins`, ` 2. Re-deploy: use firebase_hosting_deploy to push these headers live`, ` 3. Test with: https://securityheaders.com and browser DevTools Console`);
|
|
176
|
+
if (strict) {
|
|
177
|
+
report.push(``, `🔒 Strict mode enabled:`, ` - Cross-Origin-Opener-Policy: same-origin (may break popups/OAuth redirects)`, ` - Cross-Origin-Resource-Policy: same-origin (may break cross-origin assets)`, ` - upgrade-insecure-requests in CSP (forces HTTPS for all sub-resources)`, ` If you see broken features, switch to 'standard' preset.`);
|
|
178
|
+
}
|
|
179
|
+
return report.join("\n");
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=harden.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"harden.js","sourceRoot":"","sources":["../../src/tools/harden.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/tools/init.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,wBAAsB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,CA6EnE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// firebase_hosting_init - Scaffolds Firebase Hosting configuration
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { detectFramework, readFirebaseJson, writeFirebaseJson, writeFirebaseRc, fileExists, } from "../services/firebase-cli.js";
|
|
4
|
+
import { DEFAULT_SPA_CONFIG, DEFAULT_STATIC_CONFIG } from "../constants.js";
|
|
5
|
+
export async function handleInit(params) {
|
|
6
|
+
const { project_dir, project_id } = params;
|
|
7
|
+
// Check if firebase.json already exists
|
|
8
|
+
const existing = await readFirebaseJson(project_dir);
|
|
9
|
+
if (existing) {
|
|
10
|
+
return `⚠️ firebase.json already exists in ${project_dir}. Use firebase_hosting_configure to modify it, or delete it first to re-initialize.`;
|
|
11
|
+
}
|
|
12
|
+
// Detect framework if not specified
|
|
13
|
+
let framework = params.framework;
|
|
14
|
+
let publicDir = params.public_dir;
|
|
15
|
+
let isSPA = params.spa;
|
|
16
|
+
if (!framework || framework !== "static") {
|
|
17
|
+
const detected = await detectFramework(project_dir);
|
|
18
|
+
if (detected && !framework) {
|
|
19
|
+
framework = detected.framework;
|
|
20
|
+
}
|
|
21
|
+
if (detected && !publicDir) {
|
|
22
|
+
publicDir = detected.publicDir;
|
|
23
|
+
}
|
|
24
|
+
if (detected && isSPA === undefined) {
|
|
25
|
+
isSPA = detected.isSPA;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Build the config
|
|
29
|
+
const baseConfig = isSPA ? DEFAULT_SPA_CONFIG : DEFAULT_STATIC_CONFIG;
|
|
30
|
+
const config = JSON.parse(JSON.stringify(baseConfig));
|
|
31
|
+
if (publicDir) {
|
|
32
|
+
config.hosting.public = publicDir;
|
|
33
|
+
}
|
|
34
|
+
// Write firebase.json
|
|
35
|
+
await writeFirebaseJson(project_dir, config);
|
|
36
|
+
// Write .firebaserc
|
|
37
|
+
await writeFirebaseRc(project_dir, project_id);
|
|
38
|
+
// Check if .gitignore exists and add firebase cache
|
|
39
|
+
const gitignorePath = join(project_dir, ".gitignore");
|
|
40
|
+
if (await fileExists(gitignorePath)) {
|
|
41
|
+
const { readFile, appendFile } = await import("node:fs/promises");
|
|
42
|
+
const content = await readFile(gitignorePath, "utf-8");
|
|
43
|
+
if (!content.includes(".firebase")) {
|
|
44
|
+
await appendFile(gitignorePath, "\n# Firebase\n.firebase/\n");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// Build result message
|
|
48
|
+
const lines = [
|
|
49
|
+
`✅ Firebase Hosting initialized successfully!`,
|
|
50
|
+
``,
|
|
51
|
+
`Project: ${project_id}`,
|
|
52
|
+
`Directory: ${project_dir}`,
|
|
53
|
+
];
|
|
54
|
+
if (framework && framework !== "static") {
|
|
55
|
+
lines.push(`Framework detected: ${framework}`);
|
|
56
|
+
}
|
|
57
|
+
lines.push(`Public directory: ${config.hosting.public}`, `SPA mode: ${isSPA ? "enabled" : "disabled"}`, ``, `Files created:`, ` - firebase.json (hosting configuration)`, ` - .firebaserc (project alias)`, ``, `Next steps:`, ` 1. Build your project`, ` 2. Use firebase_hosting_deploy to deploy`);
|
|
58
|
+
return lines.join("\n");
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/tools/init.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/tools/preview.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,wBAAsB,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAwFzE"}
|