@canonry/canonry 7.9.0 → 7.9.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/dist/{chunk-NU62UT2S.js → chunk-4BKRZGLC.js} +63 -1
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +10 -10
|
@@ -3543,6 +3543,7 @@ import { createRequire as createRequire3 } from "module";
|
|
|
3543
3543
|
import crypto29 from "crypto";
|
|
3544
3544
|
import fs9 from "fs";
|
|
3545
3545
|
import path8 from "path";
|
|
3546
|
+
import { isIP } from "net";
|
|
3546
3547
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
3547
3548
|
import { and as and23, eq as eq29 } from "drizzle-orm";
|
|
3548
3549
|
|
|
@@ -7872,7 +7873,7 @@ function nonBlank(value) {
|
|
|
7872
7873
|
return trimmed ? trimmed : void 0;
|
|
7873
7874
|
}
|
|
7874
7875
|
function resolveBuildCommit(env = process.env) {
|
|
7875
|
-
const embedded = true ? "
|
|
7876
|
+
const embedded = true ? "1ef30fafb9a5781918fcb963ade6280231796cc9" : void 0;
|
|
7876
7877
|
return nonBlank(embedded) ?? nonBlank(env.CANONRY_COMMIT);
|
|
7877
7878
|
}
|
|
7878
7879
|
function resolveInstanceIdentity(env = process.env) {
|
|
@@ -21280,6 +21281,28 @@ function isLoopbackBindHost(host) {
|
|
|
21280
21281
|
if (/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(normalized)) return true;
|
|
21281
21282
|
return false;
|
|
21282
21283
|
}
|
|
21284
|
+
function isAllowedHost(host, allowedHosts) {
|
|
21285
|
+
const normalized = host.trim().toLowerCase().replace(/^\[|\]$/g, "");
|
|
21286
|
+
return allowedHosts.has(normalized) || isIP(normalized) !== 0;
|
|
21287
|
+
}
|
|
21288
|
+
function buildAllowedHosts(bindHost, ...configuredUrls) {
|
|
21289
|
+
const allowed = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "::1"]);
|
|
21290
|
+
if (bindHost) {
|
|
21291
|
+
const normalized = bindHost.trim().toLowerCase().replace(/^\[|\]$/g, "");
|
|
21292
|
+
if (normalized && !isLoopbackBindHost(normalized)) {
|
|
21293
|
+
allowed.add(normalized);
|
|
21294
|
+
}
|
|
21295
|
+
}
|
|
21296
|
+
for (const configuredUrl of configuredUrls) {
|
|
21297
|
+
if (!configuredUrl) continue;
|
|
21298
|
+
try {
|
|
21299
|
+
const url = new URL(configuredUrl.trim());
|
|
21300
|
+
if (url.hostname) allowed.add(url.hostname.toLowerCase().replace(/^\[|\]$/g, ""));
|
|
21301
|
+
} catch {
|
|
21302
|
+
}
|
|
21303
|
+
}
|
|
21304
|
+
return allowed;
|
|
21305
|
+
}
|
|
21283
21306
|
function resolveGooglePublicUrl(config, basePath) {
|
|
21284
21307
|
const configured = config.publicUrl?.trim();
|
|
21285
21308
|
if (configured) return configured;
|
|
@@ -21432,6 +21455,45 @@ async function createServer(opts) {
|
|
|
21432
21455
|
genReqId: () => crypto29.randomUUID(),
|
|
21433
21456
|
trustProxy
|
|
21434
21457
|
});
|
|
21458
|
+
const allowedHosts = buildAllowedHosts(opts.host, opts.config.apiUrl, opts.config.publicUrl);
|
|
21459
|
+
const embedAllowedOrigins = new Set(
|
|
21460
|
+
(embed.allowedOrigins ?? []).map((o) => {
|
|
21461
|
+
try {
|
|
21462
|
+
return new URL(o).origin;
|
|
21463
|
+
} catch {
|
|
21464
|
+
return null;
|
|
21465
|
+
}
|
|
21466
|
+
}).filter((h) => h !== null)
|
|
21467
|
+
);
|
|
21468
|
+
app.addHook("onRequest", async (req, reply) => {
|
|
21469
|
+
const reject = (message) => {
|
|
21470
|
+
const error = forbidden(message);
|
|
21471
|
+
return reply.status(error.statusCode).send(error.toJSON());
|
|
21472
|
+
};
|
|
21473
|
+
const rawHostHeader = req.headers.host?.toLowerCase() ?? "";
|
|
21474
|
+
let requestUrl;
|
|
21475
|
+
try {
|
|
21476
|
+
requestUrl = new URL(`http://${rawHostHeader}`);
|
|
21477
|
+
} catch {
|
|
21478
|
+
return reject("Unexpected Host header");
|
|
21479
|
+
}
|
|
21480
|
+
if (!isAllowedHost(requestUrl.hostname, allowedHosts)) {
|
|
21481
|
+
return reject("Unexpected Host header");
|
|
21482
|
+
}
|
|
21483
|
+
const origin = req.headers.origin;
|
|
21484
|
+
if (origin) {
|
|
21485
|
+
let originUrl;
|
|
21486
|
+
try {
|
|
21487
|
+
originUrl = new URL(origin);
|
|
21488
|
+
} catch {
|
|
21489
|
+
return reject("Invalid Origin header");
|
|
21490
|
+
}
|
|
21491
|
+
const requestHost = req.headers.host?.toLowerCase() ?? "";
|
|
21492
|
+
if (originUrl.host !== requestHost && !embedAllowedOrigins.has(originUrl.origin)) {
|
|
21493
|
+
return reject("Cross-origin request refused");
|
|
21494
|
+
}
|
|
21495
|
+
}
|
|
21496
|
+
});
|
|
21435
21497
|
const registry = new ProviderRegistry();
|
|
21436
21498
|
const providers = opts.config.providers ?? {};
|
|
21437
21499
|
if (opts.config.geminiApiKey && !providers.gemini) {
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@canonry/canonry",
|
|
3
|
-
"version": "7.9.
|
|
3
|
+
"version": "7.9.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Self-hosted AI visibility (AEO) platform: track how ChatGPT, Claude, Gemini, and Perplexity cite your domain, join it with Search Console, GA4, server-side traffic, and paid media, and fix what you find through agent tools (CLI, REST, MCP). Local SQLite.",
|
|
6
6
|
"keywords": [
|
|
@@ -86,31 +86,31 @@
|
|
|
86
86
|
"tsup": "^8.5.1",
|
|
87
87
|
"tsx": "^4.19.0",
|
|
88
88
|
"@ainyc/canonry-web": "0.0.0",
|
|
89
|
+
"@ainyc/canonry-api-routes": "0.0.0",
|
|
90
|
+
"@ainyc/canonry-config": "0.0.0",
|
|
89
91
|
"@ainyc/canonry-api-client": "0.0.0",
|
|
90
92
|
"@ainyc/canonry-contracts": "0.0.0",
|
|
91
|
-
"@ainyc/canonry-config": "0.0.0",
|
|
92
93
|
"@ainyc/canonry-db": "0.0.0",
|
|
93
|
-
"@ainyc/canonry-api-routes": "0.0.0",
|
|
94
94
|
"@ainyc/canonry-integration-bing": "0.0.0",
|
|
95
|
-
"@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
|
|
96
|
-
"@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
|
|
97
95
|
"@ainyc/canonry-integration-cloud-run": "0.0.0",
|
|
96
|
+
"@ainyc/canonry-integration-cloudflare-worker": "0.0.0",
|
|
97
|
+
"@ainyc/canonry-integration-cloudflare-queue": "0.0.0",
|
|
98
98
|
"@ainyc/canonry-integration-commoncrawl": "0.0.0",
|
|
99
|
+
"@ainyc/canonry-integration-google": "0.0.0",
|
|
99
100
|
"@ainyc/canonry-integration-google-ads": "0.0.0",
|
|
100
|
-
"@ainyc/canonry-integration-google-business-profile": "0.0.0",
|
|
101
101
|
"@ainyc/canonry-integration-google-tag-manager": "0.0.0",
|
|
102
|
-
"@ainyc/canonry-integration-google-
|
|
102
|
+
"@ainyc/canonry-integration-google-business-profile": "0.0.0",
|
|
103
103
|
"@ainyc/canonry-integration-openai-ads": "0.0.0",
|
|
104
104
|
"@ainyc/canonry-integration-traffic": "0.0.0",
|
|
105
105
|
"@ainyc/canonry-integration-wordpress": "0.0.0",
|
|
106
106
|
"@ainyc/canonry-intelligence": "0.0.0",
|
|
107
|
+
"@ainyc/canonry-integration-google-places": "0.0.0",
|
|
107
108
|
"@ainyc/canonry-provider-cdp": "0.0.0",
|
|
108
109
|
"@ainyc/canonry-provider-gemini": "0.0.0",
|
|
109
|
-
"@ainyc/canonry-integration-google": "0.0.0",
|
|
110
|
-
"@ainyc/canonry-provider-local": "0.0.0",
|
|
111
|
-
"@ainyc/canonry-provider-muse": "0.0.0",
|
|
112
110
|
"@ainyc/canonry-provider-claude": "0.0.0",
|
|
111
|
+
"@ainyc/canonry-provider-local": "0.0.0",
|
|
113
112
|
"@ainyc/canonry-provider-openai": "0.0.0",
|
|
113
|
+
"@ainyc/canonry-provider-muse": "0.0.0",
|
|
114
114
|
"@ainyc/canonry-provider-perplexity": "0.0.0",
|
|
115
115
|
"@ainyc/canonry-integration-typesafe": "0.0.0"
|
|
116
116
|
},
|