#!/usr/bin/env node // Credential helper for the Discord Sage's git verbs (row 24). git runs it // as `credential.helper` for the seat's pushes and pulls only, through the // allowlisted environment src/git.mjs builds. It answers `get` and nothing // else, for https only, from the one file named in its environment. // // Why not the fleet helper: git-credential-mosaic serves the two Gitea // hosts and declines every other host, and the host's own GitHub login // (gh) must never sign a seat's push. This helper is the seat's and reads // only the seat's token file. // // The token goes from the file to git's stdin pipe and nowhere else: not // to argv, not to stderr, not to a log. Anything wrong (a missing or loose // file, a non-https request, a value that does not look like a token) is // a short stderr line and exit 1, so git fails the operation and the verb // reports that failure masked. import { lstatSync, readFileSync } from "node:fs"; const TOKEN_FILE_ENV = "MOSAIC_DISCORD_GIT_TOKEN_FILE"; const USERNAME_ENV = "MOSAIC_DISCORD_GIT_USERNAME"; function fail(msg) { process.stderr.write(`git-credential (discord): ${msg}\n`); process.exit(1); } const action = process.argv[2]; if (action !== "get") process.exit(0); const file = process.env[TOKEN_FILE_ENV]; const username = process.env[USERNAME_ENV]; if (!file || !file.startsWith("/")) fail(`${TOKEN_FILE_ENV} is not set`); if (!username || !/^[a-z0-9][a-z0-9._-]{0,63}$/.test(username)) fail(`${USERNAME_ENV} is not set`); let input = ""; try { input = readFileSync(0, "utf8"); } catch { fail("no request on stdin"); } const req = {}; for (const line of input.split("\n")) { const i = line.indexOf("="); if (i > 0) req[line.slice(0, i)] = line.slice(i + 1); } if (req.protocol !== "https") fail("only https"); if (!req.host) fail("no host"); let st; try { st = lstatSync(file); } catch { fail("token file not found"); } if (st.isSymbolicLink() || !st.isFile()) fail("token file must be a regular file"); if ((st.mode & 0o777) !== 0o600) fail("token file must be mode 0600"); const token = readFileSync(file, "utf8").trim(); if (!/^[A-Za-z0-9_]{20,}$/.test(token)) fail("token file does not hold a token"); process.stdout.write(`username=${username}\npassword=${token}\n`);