@calimero-network/registry-cli 1.22.4 → 1.23.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/dist/index.js +85 -17
- package/dist/index.js.map +1 -1
- package/package.json +1 -3
package/dist/index.js
CHANGED
|
@@ -98,10 +98,24 @@ var RemoteConfig = class _RemoteConfig {
|
|
|
98
98
|
return false;
|
|
99
99
|
}
|
|
100
100
|
if (parsed.protocol !== "http:") return false;
|
|
101
|
-
const host = parsed.hostname.toLowerCase();
|
|
101
|
+
const host = parsed.hostname.toLowerCase().replace(/^\[(.*)\]$/, "$1");
|
|
102
102
|
const isLocal = host === "localhost" || host === "127.0.0.1" || host === "::1" || host.endsWith(".localhost");
|
|
103
103
|
return !isLocal;
|
|
104
104
|
}
|
|
105
|
+
/**
|
|
106
|
+
* Throws when an API key would be sent to `url` over plain http:// to a
|
|
107
|
+
* non-loopback host. Called wherever the effective registry URL (flag, env
|
|
108
|
+
* var or config file) is about to receive the Bearer key. https:// and
|
|
109
|
+
* http://localhost / 127.0.0.1 / [::1] are always allowed.
|
|
110
|
+
*/
|
|
111
|
+
static assertApiKeyTransport(url, apiKey) {
|
|
112
|
+
if (!apiKey) return;
|
|
113
|
+
if (_RemoteConfig.isInsecureRemoteUrl(url)) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Refusing to send the API key to ${url}: plain http:// is only allowed for localhost / 127.0.0.1 / [::1]. Use an https:// registry URL (check --url, CALIMERO_REGISTRY_URL and \`calimero-registry config get registry-url\`).`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
105
119
|
/**
|
|
106
120
|
* Get API key with priority:
|
|
107
121
|
* 1. Environment variable CALIMERO_API_KEY
|
|
@@ -776,6 +790,18 @@ var LocalArtifactServer = class {
|
|
|
776
790
|
};
|
|
777
791
|
}
|
|
778
792
|
};
|
|
793
|
+
function isAllowedOrigin(origin, allowedHosts) {
|
|
794
|
+
let parsed;
|
|
795
|
+
try {
|
|
796
|
+
parsed = new URL(origin);
|
|
797
|
+
} catch {
|
|
798
|
+
return false;
|
|
799
|
+
}
|
|
800
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
801
|
+
return false;
|
|
802
|
+
}
|
|
803
|
+
return allowedHosts.has(parsed.hostname.toLowerCase());
|
|
804
|
+
}
|
|
779
805
|
var LocalRegistryServer = class {
|
|
780
806
|
config;
|
|
781
807
|
dataStore;
|
|
@@ -821,6 +847,43 @@ var LocalRegistryServer = class {
|
|
|
821
847
|
});
|
|
822
848
|
}
|
|
823
849
|
});
|
|
850
|
+
const allowedOriginHosts = /* @__PURE__ */ new Set();
|
|
851
|
+
for (const name of allowedHosts) {
|
|
852
|
+
allowedOriginHosts.add(name.replace(/:\d+$/, ""));
|
|
853
|
+
}
|
|
854
|
+
this.server.addHook("onRequest", async (request, reply) => {
|
|
855
|
+
const method = request.method.toUpperCase();
|
|
856
|
+
if (method === "GET" || method === "HEAD" || method === "OPTIONS") {
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
const origin = request.headers.origin;
|
|
860
|
+
if (origin !== void 0 && !isAllowedOrigin(origin, allowedOriginHosts)) {
|
|
861
|
+
return reply.code(403).send({
|
|
862
|
+
statusCode: 403,
|
|
863
|
+
error: "Forbidden",
|
|
864
|
+
message: "Cross-origin request not allowed"
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
const fetchSite = request.headers["sec-fetch-site"];
|
|
868
|
+
if (fetchSite !== void 0 && fetchSite !== "same-origin" && fetchSite !== "none") {
|
|
869
|
+
return reply.code(403).send({
|
|
870
|
+
statusCode: 403,
|
|
871
|
+
error: "Forbidden",
|
|
872
|
+
message: "Cross-site request not allowed"
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
const pathname = request.url.split("?")[0];
|
|
876
|
+
if (pathname.startsWith("/local/")) {
|
|
877
|
+
const contentType = String(request.headers["content-type"] ?? "").split(";")[0].trim().toLowerCase();
|
|
878
|
+
if (contentType !== "application/json") {
|
|
879
|
+
return reply.code(415).send({
|
|
880
|
+
statusCode: 415,
|
|
881
|
+
error: "Unsupported Media Type",
|
|
882
|
+
message: "Content-Type must be application/json"
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
});
|
|
824
887
|
await this.registerRoutes();
|
|
825
888
|
await this.server.listen({ port: serverPort, host });
|
|
826
889
|
this.isRunning = true;
|
|
@@ -1755,11 +1818,12 @@ Note:
|
|
|
1755
1818
|
- Config file values are used unless overridden by flags or environment variables
|
|
1756
1819
|
- Priority: flag > environment variable > config file > default
|
|
1757
1820
|
`
|
|
1758
|
-
).action(async (bundleFile, options) => {
|
|
1821
|
+
).action(async (bundleFile, options, command) => {
|
|
1822
|
+
const urlFlag = command.optsWithGlobals().url;
|
|
1759
1823
|
let mpkPath;
|
|
1760
1824
|
let tempMpk = null;
|
|
1761
1825
|
try {
|
|
1762
|
-
if (!options.remote && (
|
|
1826
|
+
if (!options.remote && (urlFlag || options.apiKey)) {
|
|
1763
1827
|
console.warn(
|
|
1764
1828
|
"\u26A0\uFE0F Warning: --url and --api-key are only used with --remote flag"
|
|
1765
1829
|
);
|
|
@@ -1889,8 +1953,9 @@ Note:
|
|
|
1889
1953
|
);
|
|
1890
1954
|
} else if (useRemote) {
|
|
1891
1955
|
const remoteConfig = new RemoteConfig();
|
|
1892
|
-
const registryUrl =
|
|
1956
|
+
const registryUrl = urlFlag || process.env.CALIMERO_REGISTRY_URL || remoteConfig.getRegistryUrl();
|
|
1893
1957
|
const apiKey = options.apiKey || process.env.CALIMERO_API_KEY || remoteConfig.getApiKey();
|
|
1958
|
+
RemoteConfig.assertApiKeyTransport(registryUrl, apiKey);
|
|
1894
1959
|
await pushToRemote(mpkPath, manifest, registryUrl, apiKey);
|
|
1895
1960
|
}
|
|
1896
1961
|
} catch (error) {
|
|
@@ -1949,7 +2014,8 @@ Examples:
|
|
|
1949
2014
|
# 3) With custom registry URL:
|
|
1950
2015
|
$ calimero-registry bundle edit com.calimero.myapp 1.0.0 --remote --url https://apps.calimero.network --manifest signed-manifest.json
|
|
1951
2016
|
`
|
|
1952
|
-
).action(async (pkg, version2, options) => {
|
|
2017
|
+
).action(async (pkg, version2, options, command) => {
|
|
2018
|
+
const urlFlag = command.optsWithGlobals().url;
|
|
1953
2019
|
try {
|
|
1954
2020
|
if (!options.remote) {
|
|
1955
2021
|
console.error(
|
|
@@ -1958,8 +2024,9 @@ Examples:
|
|
|
1958
2024
|
process.exit(1);
|
|
1959
2025
|
}
|
|
1960
2026
|
const remoteConfig = new RemoteConfig();
|
|
1961
|
-
const registryUrl =
|
|
2027
|
+
const registryUrl = urlFlag || process.env.CALIMERO_REGISTRY_URL || remoteConfig.getRegistryUrl();
|
|
1962
2028
|
const apiKey = options.apiKey || process.env.CALIMERO_API_KEY || remoteConfig.getApiKey();
|
|
2029
|
+
RemoteConfig.assertApiKeyTransport(registryUrl, apiKey);
|
|
1963
2030
|
const baseUrl = registryUrl.replace(/\/$/, "");
|
|
1964
2031
|
const bundleUrl = `${baseUrl}/api/v2/bundles/${encodeURIComponent(pkg)}/${encodeURIComponent(version2)}`;
|
|
1965
2032
|
const headers = {
|
|
@@ -2426,7 +2493,7 @@ function getGlobalOpts(command) {
|
|
|
2426
2493
|
timeout: parseInt(String(opts.timeout ?? DEFAULT_TIMEOUT), 10) || DEFAULT_TIMEOUT
|
|
2427
2494
|
};
|
|
2428
2495
|
}
|
|
2429
|
-
function getAuthHeaders() {
|
|
2496
|
+
function getAuthHeaders(url) {
|
|
2430
2497
|
const remoteConfig = new RemoteConfig();
|
|
2431
2498
|
const apiKey = remoteConfig.getApiKey();
|
|
2432
2499
|
if (!apiKey) {
|
|
@@ -2434,6 +2501,7 @@ function getAuthHeaders() {
|
|
|
2434
2501
|
"API token required for org write operations.\nGet a token from the Organizations page in the web UI, then run:\n calimero-registry config set api-key <token>"
|
|
2435
2502
|
);
|
|
2436
2503
|
}
|
|
2504
|
+
RemoteConfig.assertApiKeyTransport(url, apiKey);
|
|
2437
2505
|
return { Authorization: `Bearer ${apiKey}` };
|
|
2438
2506
|
}
|
|
2439
2507
|
async function fetchJson(url, options = {}) {
|
|
@@ -2461,9 +2529,9 @@ var orgCommand = new Command("org").description("Manage organizations (create, l
|
|
|
2461
2529
|
const spinner2 = ora4("Resolving identity...").start();
|
|
2462
2530
|
let authHeaders;
|
|
2463
2531
|
try {
|
|
2464
|
-
authHeaders = getAuthHeaders();
|
|
2532
|
+
authHeaders = getAuthHeaders(url);
|
|
2465
2533
|
} catch (e) {
|
|
2466
|
-
spinner2.fail("API token
|
|
2534
|
+
spinner2.fail("Cannot use API token");
|
|
2467
2535
|
console.error(chalk4.red(e instanceof Error ? e.message : String(e)));
|
|
2468
2536
|
process.exit(1);
|
|
2469
2537
|
}
|
|
@@ -2538,7 +2606,7 @@ var orgCommand = new Command("org").description("Manage organizations (create, l
|
|
|
2538
2606
|
const { url, timeout } = getGlobalOpts(command);
|
|
2539
2607
|
const spinner2 = ora4("Creating organization...").start();
|
|
2540
2608
|
try {
|
|
2541
|
-
const authHeaders = getAuthHeaders();
|
|
2609
|
+
const authHeaders = getAuthHeaders(url);
|
|
2542
2610
|
const pathname = "/api/v2/orgs";
|
|
2543
2611
|
const body = {
|
|
2544
2612
|
name: options.name.trim(),
|
|
@@ -2599,7 +2667,7 @@ var orgCommand = new Command("org").description("Manage organizations (create, l
|
|
|
2599
2667
|
const { url, timeout } = getGlobalOpts(command);
|
|
2600
2668
|
const spinner2 = ora4("Updating organization...").start();
|
|
2601
2669
|
try {
|
|
2602
|
-
const authHeaders = getAuthHeaders();
|
|
2670
|
+
const authHeaders = getAuthHeaders(url);
|
|
2603
2671
|
const pathname = `/api/v2/orgs/${encodeURIComponent(orgId)}`;
|
|
2604
2672
|
const body = {};
|
|
2605
2673
|
if (options.name !== void 0) body.name = options.name;
|
|
@@ -2664,7 +2732,7 @@ var orgCommand = new Command("org").description("Manage organizations (create, l
|
|
|
2664
2732
|
}
|
|
2665
2733
|
const spinner2 = ora4("Deleting organization...").start();
|
|
2666
2734
|
try {
|
|
2667
|
-
const authHeaders = getAuthHeaders();
|
|
2735
|
+
const authHeaders = getAuthHeaders(url);
|
|
2668
2736
|
const pathname = `/api/v2/orgs/${encodeURIComponent(orgId)}`;
|
|
2669
2737
|
const base = url.replace(/\/$/, "");
|
|
2670
2738
|
const { data, status } = await fetchJson(`${base}${pathname}`, {
|
|
@@ -2720,7 +2788,7 @@ var orgCommand = new Command("org").description("Manage organizations (create, l
|
|
|
2720
2788
|
const { url, timeout } = getGlobalOpts(command);
|
|
2721
2789
|
const spinner2 = ora4("Adding member...").start();
|
|
2722
2790
|
try {
|
|
2723
|
-
const authHeaders = getAuthHeaders();
|
|
2791
|
+
const authHeaders = getAuthHeaders(url);
|
|
2724
2792
|
const pathname = `/api/v2/orgs/${encodeURIComponent(orgId)}/members`;
|
|
2725
2793
|
const body = {
|
|
2726
2794
|
email: email.trim(),
|
|
@@ -2757,7 +2825,7 @@ var orgCommand = new Command("org").description("Manage organizations (create, l
|
|
|
2757
2825
|
const role = options.role === "admin" ? "admin" : "member";
|
|
2758
2826
|
const spinner2 = ora4("Updating member role...").start();
|
|
2759
2827
|
try {
|
|
2760
|
-
const authHeaders = getAuthHeaders();
|
|
2828
|
+
const authHeaders = getAuthHeaders(url);
|
|
2761
2829
|
const pathname = `/api/v2/orgs/${encodeURIComponent(orgId)}/members/${encodeURIComponent(email.trim())}`;
|
|
2762
2830
|
const body = { role };
|
|
2763
2831
|
const base = url.replace(/\/$/, "");
|
|
@@ -2790,7 +2858,7 @@ var orgCommand = new Command("org").description("Manage organizations (create, l
|
|
|
2790
2858
|
const { url, timeout } = getGlobalOpts(command);
|
|
2791
2859
|
const spinner2 = ora4("Removing member...").start();
|
|
2792
2860
|
try {
|
|
2793
|
-
const authHeaders = getAuthHeaders();
|
|
2861
|
+
const authHeaders = getAuthHeaders(url);
|
|
2794
2862
|
const pathname = `/api/v2/orgs/${encodeURIComponent(orgId)}/members/${encodeURIComponent(email.trim())}`;
|
|
2795
2863
|
const base = url.replace(/\/$/, "");
|
|
2796
2864
|
const { data, status } = await fetchJson(`${base}${pathname}`, {
|
|
@@ -2823,7 +2891,7 @@ var orgCommand = new Command("org").description("Manage organizations (create, l
|
|
|
2823
2891
|
const { url, timeout } = getGlobalOpts(command);
|
|
2824
2892
|
const spinner2 = ora4("Linking package...").start();
|
|
2825
2893
|
try {
|
|
2826
|
-
const authHeaders = getAuthHeaders();
|
|
2894
|
+
const authHeaders = getAuthHeaders(url);
|
|
2827
2895
|
const pathname = `/api/v2/orgs/${encodeURIComponent(orgId)}/packages`;
|
|
2828
2896
|
const body = { package: pkg.trim() };
|
|
2829
2897
|
const base = url.replace(/\/$/, "");
|
|
@@ -2856,7 +2924,7 @@ var orgCommand = new Command("org").description("Manage organizations (create, l
|
|
|
2856
2924
|
const { url, timeout } = getGlobalOpts(command);
|
|
2857
2925
|
const spinner2 = ora4("Unlinking package...").start();
|
|
2858
2926
|
try {
|
|
2859
|
-
const authHeaders = getAuthHeaders();
|
|
2927
|
+
const authHeaders = getAuthHeaders(url);
|
|
2860
2928
|
const pathname = `/api/v2/orgs/${encodeURIComponent(orgId)}/packages/${encodeURIComponent(pkg.trim())}`;
|
|
2861
2929
|
const base = url.replace(/\/$/, "");
|
|
2862
2930
|
const { data, status } = await fetchJson(`${base}${pathname}`, {
|