@exposify/cli 0.0.5 → 0.0.6
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 +35 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1232,7 +1232,7 @@ function copyToClipboard(text, deps = {}) {
|
|
|
1232
1232
|
|
|
1233
1233
|
// src/sign-in.ts
|
|
1234
1234
|
import { spawn } from "child_process";
|
|
1235
|
-
import { existsSync as existsSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
1235
|
+
import { chmodSync as chmodSync2, existsSync as existsSync2, readFileSync as readFileSync6, statSync, writeFileSync as writeFileSync5 } from "fs";
|
|
1236
1236
|
import { join as join3 } from "path";
|
|
1237
1237
|
import { parseEnv as parseEnv2 } from "util";
|
|
1238
1238
|
import { authStacksOf, dependencyNames } from "@exposify/core";
|
|
@@ -1265,11 +1265,16 @@ function writeKeyToEnv(root, key) {
|
|
|
1265
1265
|
const path = join3(root, ".env");
|
|
1266
1266
|
const line = `${KEY_ENV}=${key}`;
|
|
1267
1267
|
let created = false;
|
|
1268
|
+
let tightened = false;
|
|
1268
1269
|
if (existsSync2(path)) {
|
|
1269
1270
|
const text = readFileSync6(path, "utf8");
|
|
1270
1271
|
const re = new RegExp(`^\\s*(export\\s+)?${KEY_ENV}\\s*=.*$`, "m");
|
|
1271
1272
|
const next = re.test(text) ? text.replace(re, line) : `${text}${text === "" || text.endsWith("\n") ? "" : "\n"}${line}
|
|
1272
1273
|
`;
|
|
1274
|
+
if (process.platform !== "win32" && (statSync(path).mode & 63) !== 0) {
|
|
1275
|
+
chmodSync2(path, 384);
|
|
1276
|
+
tightened = true;
|
|
1277
|
+
}
|
|
1273
1278
|
writeFileSync5(path, next);
|
|
1274
1279
|
} else {
|
|
1275
1280
|
writeFileSync5(path, `${line}
|
|
@@ -1281,7 +1286,22 @@ function writeKeyToEnv(root, key) {
|
|
|
1281
1286
|
ignored = readFileSync6(join3(root, ".gitignore"), "utf8").split("\n").map((l) => l.trim()).some((l) => [".env", "/.env", ".env*", "/.env*", "*.env"].includes(l));
|
|
1282
1287
|
} catch {
|
|
1283
1288
|
}
|
|
1284
|
-
return { path, created, ignored };
|
|
1289
|
+
return { path, created, tightened, ignored };
|
|
1290
|
+
}
|
|
1291
|
+
var DASHBOARD_HOSTS = ["dashboard.agemno.com"];
|
|
1292
|
+
function isSafeVerifyUrl(verifyUrl, apiUrl) {
|
|
1293
|
+
if (/[\s&|^<>"'`%;$\\]/.test(verifyUrl)) return false;
|
|
1294
|
+
let url;
|
|
1295
|
+
try {
|
|
1296
|
+
url = new URL(verifyUrl);
|
|
1297
|
+
} catch {
|
|
1298
|
+
return false;
|
|
1299
|
+
}
|
|
1300
|
+
const isLocal = (u) => u.hostname === "localhost" || u.hostname === "127.0.0.1" || u.hostname === "[::1]";
|
|
1301
|
+
if (!(url.protocol === "https:" || url.protocol === "http:" && isLocal(url))) return false;
|
|
1302
|
+
if (url.username || url.password) return false;
|
|
1303
|
+
const api = new URL(apiUrl);
|
|
1304
|
+
return url.host === api.host || DASHBOARD_HOSTS.includes(url.host) || isLocal(url) && isLocal(api);
|
|
1285
1305
|
}
|
|
1286
1306
|
async function signInToExposify(deps) {
|
|
1287
1307
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
@@ -1302,6 +1322,13 @@ async function signInToExposify(deps) {
|
|
|
1302
1322
|
} catch (err) {
|
|
1303
1323
|
return { ok: false, reason: "unreachable", message: `couldn't reach ${deps.apiUrl} (${err.message})` };
|
|
1304
1324
|
}
|
|
1325
|
+
if (typeof started.verifyUrl !== "string" || !isSafeVerifyUrl(started.verifyUrl, deps.apiUrl)) {
|
|
1326
|
+
return {
|
|
1327
|
+
ok: false,
|
|
1328
|
+
reason: "unsafe",
|
|
1329
|
+
message: `${deps.apiUrl} sent a sign-in link that isn't on its own host or ${DASHBOARD_HOSTS.join(", ")}, so it wasn't opened`
|
|
1330
|
+
};
|
|
1331
|
+
}
|
|
1305
1332
|
const opened = (deps.openUrl ?? openInBrowser)(started.verifyUrl);
|
|
1306
1333
|
deps.show({ userCode: started.userCode, verifyUrl: started.verifyUrl, opened });
|
|
1307
1334
|
const deadline = Date.now() + started.expiresIn * 1e3;
|
|
@@ -1331,9 +1358,13 @@ async function signInToExposify(deps) {
|
|
|
1331
1358
|
}
|
|
1332
1359
|
return { ok: false, reason: "expired", message: "the sign-in link expired" };
|
|
1333
1360
|
}
|
|
1361
|
+
function browserCommand(url, platform = process.platform) {
|
|
1362
|
+
if (platform === "win32") return ["rundll32", "url.dll,FileProtocolHandler", url];
|
|
1363
|
+
return platform === "darwin" ? ["open", url] : ["xdg-open", url];
|
|
1364
|
+
}
|
|
1334
1365
|
function openInBrowser(url) {
|
|
1335
1366
|
if (process.env.EXPOSIFY_NO_BROWSER === "1") return false;
|
|
1336
|
-
const [cmd, ...args] =
|
|
1367
|
+
const [cmd, ...args] = browserCommand(url);
|
|
1337
1368
|
try {
|
|
1338
1369
|
const child = spawn(cmd, args, { stdio: "ignore", detached: true });
|
|
1339
1370
|
child.on("error", () => {
|
|
@@ -1984,6 +2015,7 @@ ${bold(title)} \xB7 How your users sign in`);
|
|
|
1984
2015
|
const written = writeKeyToEnv(root, outcome.key);
|
|
1985
2016
|
process.env[KEY_ENV] = outcome.key;
|
|
1986
2017
|
console.log(green(` \u2713 ${written.created ? "Created" : "Updated"} ${relative(cwd, written.path) || written.path} with ${KEY_ENV}`) + ` (project "${outcome.projectName}").`);
|
|
2018
|
+
if (written.tightened) console.log(dim(` .env could be read by other users on this machine; it's owner-only now.`));
|
|
1987
2019
|
if (!written.ignored) console.log(yellow(` ! Add .env to .gitignore: it now holds a secret.`));
|
|
1988
2020
|
if (own) console.log(` Your app tells Exposify about its ${sb ? "Supabase Auth" : "login"} when it starts with ${KEY_ENV}: nothing to add in the dashboard.`);
|
|
1989
2021
|
else {
|