@bobfrankston/npmglobalize 1.0.115 → 1.0.116

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 (3) hide show
  1. package/README.md +10 -0
  2. package/lib.js +37 -16
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -127,6 +127,16 @@ npmglobalize --fix # Runs npm audit fix
127
127
  npmglobalize --no-fix
128
128
  ```
129
129
 
130
+ ### 🔑 OAuth Credentials Handling
131
+
132
+ `npmglobalize` automatically detects `credentials.json` files and handles them based on OAuth app type:
133
+
134
+ - **Desktop/installed apps** (`"installed"` key in JSON): The `client_secret` is just a public app registration ID, not a real secret. Google's own docs state: *"the client_secret is obviously not treated as a secret."* These files are kept in the repo — `!credentials.json` is added to `.gitignore`/`.npmignore` to override any broader ignore patterns.
135
+
136
+ - **Web apps** (`"web"` key in JSON): The `client_secret` is a real secret. These files are automatically added to `.gitignore`/`.npmignore` to prevent accidental exposure.
137
+
138
+ This distinction also drives the **push protection auto-bypass**: when GitHub blocks a push because it detects an OAuth client secret, `npmglobalize` checks the credential file type. For installed apps, it auto-bypasses (marking as `false_positive`) since the credential is public by design.
139
+
130
140
  ### 🔄 File Reference Management
131
141
 
132
142
  **Default behavior** (restore file: references after publish):
package/lib.js CHANGED
@@ -1440,30 +1440,51 @@ function parsePushProtection(errorOutput, cwd) {
1440
1440
  return result;
1441
1441
  }
1442
1442
  result.detected = true;
1443
- // Split on secret-type headers: "—— Secret Type ————..."
1444
- const blocks = cleaned.split(/\u2014\u2014\s+/).slice(1);
1445
- for (const block of blocks) {
1446
- const typeMatch = block.match(/^(.+?)[\s\u2014]+/);
1447
- const type = typeMatch ? typeMatch[1].trim() : '';
1448
- if (!type)
1449
- continue;
1450
- const pathMatch = block.match(/path:\s+(\S+?)(?::(\d+))?\s/);
1451
- const file = pathMatch ? pathMatch[1].trim() : '';
1452
- const urlMatch = block.match(/(https:\/\/github\.com\/\S+\/unblock-secret\/\S+)/);
1453
- const unblockUrl = urlMatch ? urlMatch[1].trim() : '';
1454
- if (type && (file || unblockUrl)) {
1455
- result.secrets.push({ type, file, unblockUrl });
1443
+ // Match secret blocks using a single regex across any dash characters
1444
+ // Pattern: "-- Type Name ---..." then "path: file:line" then "unblock-secret/id" URL
1445
+ // Handles em-dash (—), en-dash (–), and regular dash (-)
1446
+ const secretRegex = /[-\u2014\u2013]{2,}\s+(.+?)\s+[-\u2014\u2013]{2,}[\s\S]*?path:\s+(\S+?)(?::\d+)?\s[\s\S]*?(https:\/\/github\.com\/\S+\/unblock-secret\/\S+)/g;
1447
+ let match;
1448
+ while ((match = secretRegex.exec(cleaned)) !== null) {
1449
+ result.secrets.push({
1450
+ type: match[1].trim(),
1451
+ file: match[2].trim(),
1452
+ unblockUrl: match[3].trim()
1453
+ });
1454
+ }
1455
+ // If regex didn't match (encoding issues), fall back to finding unblock URLs + paths
1456
+ if (result.secrets.length === 0) {
1457
+ const urlRegex = /(https:\/\/github\.com\/\S+\/unblock-secret\/\S+)/g;
1458
+ const pathRegex = /path:\s+(\S+?)(?::\d+)?\s/g;
1459
+ const urls = [];
1460
+ const files = [];
1461
+ let m;
1462
+ while ((m = urlRegex.exec(cleaned)) !== null)
1463
+ urls.push(m[1].trim());
1464
+ while ((m = pathRegex.exec(cleaned)) !== null)
1465
+ files.push(m[1].trim());
1466
+ // Try to detect secret type from text
1467
+ const hasOAuth = cleaned.toLowerCase().includes('oauth');
1468
+ const secretType = hasOAuth ? 'Google OAuth Credential' : 'Secret';
1469
+ for (let i = 0; i < Math.max(urls.length, files.length); i++) {
1470
+ result.secrets.push({
1471
+ type: secretType,
1472
+ file: files[i] || '',
1473
+ unblockUrl: urls[i] || ''
1474
+ });
1456
1475
  }
1457
1476
  }
1458
1477
  // Check if all detected secrets are from Google OAuth installed apps (safe to include)
1459
1478
  if (result.secrets.length > 0) {
1460
1479
  result.allInstalledOAuth = result.secrets.every(s => {
1461
- if (!s.type.toLowerCase().includes('oauth'))
1462
- return false;
1463
1480
  if (!s.file)
1464
1481
  return false;
1482
+ // Check by type name OR by inspecting the actual credential file
1465
1483
  const credType = detectCredentialsTypeFromFile(path.join(cwd, s.file));
1466
- return credType === 'installed';
1484
+ if (credType === 'installed')
1485
+ return true;
1486
+ // Also match if the type name mentions OAuth (even if file check failed)
1487
+ return s.type.toLowerCase().includes('oauth') && credType !== 'web';
1467
1488
  });
1468
1489
  }
1469
1490
  return result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bobfrankston/npmglobalize",
3
- "version": "1.0.115",
3
+ "version": "1.0.116",
4
4
  "description": "Transform file: dependencies to npm versions for publishing",
5
5
  "main": "index.js",
6
6
  "type": "module",