@jondotsoy/envs 0.1.9-alpha.0 → 0.1.9-alpha.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.
Files changed (2) hide show
  1. package/envs.js +20 -11
  2. package/package.json +1 -1
package/envs.js CHANGED
@@ -17,11 +17,17 @@ function dotenvProblem(key, text) {
17
17
  if (!/^[\w.-]+$/.test(key)) {
18
18
  return "the name is not a valid variable name (only letters, digits, `_`, `.` and `-`)";
19
19
  }
20
+ if (text.includes("'") && text.includes('"') && text.includes("`")) {
21
+ return "the value has single, double and backtick quotes at once, so no .env quoting can hold it as is";
22
+ }
20
23
  if (/[\r\n]/.test(text)) {
21
24
  return "the value has a line break, which a .env file cannot hold safely (write it on one line, with `\\n` escapes if the application expects them)";
22
25
  }
23
26
  return;
24
27
  }
28
+ function hasSecretPattern(text) {
29
+ return SECRET_PATTERNS.some(([pattern]) => pattern.test(text));
30
+ }
25
31
  function isSensitiveKey(key) {
26
32
  return SENSITIVE_KEY.test(key);
27
33
  }
@@ -130,13 +136,10 @@ var init_lint_rules = __esm(() => {
130
136
  },
131
137
  {
132
138
  id: "open-permissions",
133
- description: "values.yml or the history is readable by other users.",
134
- check: (ctx) => [
135
- { file: valuesFile, mode: ctx.valuesMode },
136
- ...ctx.hasHistory ? [{ file: historyFile, mode: ctx.historyMode }] : []
137
- ].filter(({ mode }) => mode !== undefined && (mode & 63) !== 0).map(({ file, mode }) => ({
138
- location: file,
139
- message: `${file.slice(".envs/".length)} is readable by other users (mode ${(mode & 511).toString(8)}). Run \`chmod 600 ${file}\`.`
139
+ description: "values.yml, the history or a worktree's .env is readable by other users.",
140
+ check: (ctx) => envFiles(ctx).filter(({ mode }) => mode !== undefined && (mode & 63) !== 0).map(({ location, mode }) => ({
141
+ location,
142
+ message: `${location.replace(/^\.envs\//, "")} is readable by other users (mode ${(mode & 511).toString(8)}). Run \`chmod 600 ${location}\`.`
140
143
  }))
141
144
  },
142
145
  {
@@ -890,9 +893,13 @@ function parseValue(value) {
890
893
  return value;
891
894
  }
892
895
  function formatDotenvValue(value) {
893
- if (value !== "" && !/[\s#"'\\$]/.test(value))
896
+ if (value !== "" && !/[\s#"'`\\$]/.test(value))
894
897
  return value;
895
- return value.includes('"') ? `'${value}'` : `"${value}"`;
898
+ if (!value.includes('"') && !/\\[nr]/.test(value))
899
+ return `"${value}"`;
900
+ if (!value.includes("'"))
901
+ return `'${value}'`;
902
+ return `\`${value}\``;
896
903
  }
897
904
  function updateDotenv(text, entries, remove = new Set) {
898
905
  const pending = new Map(Object.entries(entries));
@@ -1142,7 +1149,9 @@ async function pushLocked(ctx, only) {
1142
1149
  }
1143
1150
  for (const key of orphans)
1144
1151
  removals.set(key, [...removals.get(key) ?? [], label]);
1145
- await writeFile(join2(path, ".env"), updateDotenv(current, entries, orphans), { mode: 384 });
1152
+ const dotenvPath = join2(path, ".env");
1153
+ await writeFile(dotenvPath, updateDotenv(current, entries, orphans), { mode: 384 });
1154
+ await chmod(dotenvPath, 384).catch(() => {});
1146
1155
  }
1147
1156
  for (const [line, names] of changes) {
1148
1157
  ctx.log(yellow(`\u21BB ${line} \u2192 ${names.join(", ")}`));
@@ -1544,7 +1553,7 @@ Commands:
1544
1553
  history Manage the history (history redact: remove secrets in clear from an existing history)
1545
1554
  config Read or change .envs/config.yml (config set <key> <value>, config get [key], config delete <key>)
1546
1555
  help Show this help message
1547
- `, colorsDisabled = () => Boolean(process.env.NO_COLOR), MASK = "********", isMaskedKey = (key) => isSensitiveKey(key) || /(^|_)KEY(_|$)/i.test(key), URL_SECRET, hasUrlSecret = (value) => URL_SECRET.test(value) || /^[a-z][a-z0-9+.-]*:\/\//i.test(value) && sensitiveQuery(value), sensitiveQuery = (value) => [...value.matchAll(/[?&]([^=&\s#]+)=([^&\s#]*)/g)].some(([, name]) => isMaskedKey(name.toUpperCase().replace(/[^A-Z0-9]+/g, "_"))), maskUrlSecrets = (value) => value.replace(URL_SECRET, `$1${MASK}$3`).replace(/([?&])([^=&\s#]+)=([^&\s#]*)/g, (all, sep, name) => isMaskedKey(name.toUpperCase().replace(/[^A-Z0-9]+/g, "_")) ? `${sep}${name}=${MASK}` : all), isSecretEntry = (name, value) => isMaskedKey(name) || hasUrlSecret(value), displayValue = (name, value) => isMaskedKey(name) ? MASK : formatDotenvValue(maskUrlSecrets(value)), paint = (code, text) => colorsDisabled() ? text : `\x1B[${code}m${text}\x1B[0m`, green = (text) => paint(32, text), yellow = (text) => paint(33, text), red = (text) => paint(31, text), defaultContext = () => ({
1556
+ `, colorsDisabled = () => Boolean(process.env.NO_COLOR), MASK = "********", isMaskedKey = (key) => isSensitiveKey(key) || /(^|_)KEY(_|$)/i.test(key), URL_SECRET, hasUrlSecret = (value) => URL_SECRET.test(value) || /^[a-z][a-z0-9+.-]*:\/\//i.test(value) && sensitiveQuery(value), sensitiveQuery = (value) => [...value.matchAll(/[?&]([^=&\s#]+)=([^&\s#]*)/g)].some(([, name]) => isMaskedKey(name.toUpperCase().replace(/[^A-Z0-9]+/g, "_"))), maskUrlSecrets = (value) => value.replace(URL_SECRET, `$1${MASK}$3`).replace(/([?&])([^=&\s#]+)=([^&\s#]*)/g, (all, sep, name) => isMaskedKey(name.toUpperCase().replace(/[^A-Z0-9]+/g, "_")) ? `${sep}${name}=${MASK}` : all), isSecretEntry = (name, value) => isMaskedKey(name) || hasUrlSecret(value) || hasSecretPattern(value), displayValue = (name, value) => isMaskedKey(name) || hasSecretPattern(value) ? MASK : formatDotenvValue(maskUrlSecrets(value)), paint = (code, text) => colorsDisabled() ? text : `\x1B[${code}m${text}\x1B[0m`, green = (text) => paint(32, text), yellow = (text) => paint(33, text), red = (text) => paint(31, text), defaultContext = () => ({
1548
1557
  cwd: process.cwd(),
1549
1558
  log: (message) => console.log(message),
1550
1559
  error: (message) => console.error(message),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jondotsoy/envs",
3
- "version": "0.1.9-alpha.0",
3
+ "version": "0.1.9-alpha.1",
4
4
  "license": "MIT",
5
5
  "author": {
6
6
  "name": "Jonathan Delgado",