@nodatachat/guard 2.4.0 → 2.5.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.
Files changed (2) hide show
  1. package/dist/cli.js +176 -1
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -26,7 +26,7 @@ const reporter_1 = require("./reporter");
26
26
  const scheduler_1 = require("./fixers/scheduler");
27
27
  const vault_crypto_1 = require("./vault-crypto");
28
28
  const capsule_dir_1 = require("./capsule-dir");
29
- const VERSION = "2.4.0";
29
+ const VERSION = "2.5.0";
30
30
  async function main() {
31
31
  const args = process.argv.slice(2);
32
32
  // ── Subcommand routing ──
@@ -114,6 +114,49 @@ async function main() {
114
114
  licenseKey = process.env.NDC_LICENSE || process.env.NODATA_LICENSE_KEY || process.env.NODATA_API_KEY || process.env.NDC_API_KEY;
115
115
  if (!dbUrl)
116
116
  dbUrl = process.env.DATABASE_URL;
117
+ // ── Auto-detect from .env files ──
118
+ // Guard reads .env files to find DATABASE_URL and license keys.
119
+ // ALL values stay local — never sent anywhere.
120
+ // If DB URL is found, Guard asks for explicit consent before connecting.
121
+ if (!dbUrl || !licenseKey) {
122
+ const envResult = readEnvFiles(projectDir);
123
+ if (!licenseKey && envResult.licenseKey) {
124
+ licenseKey = envResult.licenseKey;
125
+ if (!ciMode)
126
+ console.log(`\n \x1b[32m✓\x1b[0m Found license key in ${envResult.licenseSource}`);
127
+ }
128
+ if (!dbUrl && envResult.dbUrl) {
129
+ // Mask the connection string for display (show host only)
130
+ const masked = maskDbUrl(envResult.dbUrl);
131
+ if (ciMode) {
132
+ // CI mode: auto-consent (operator set up the env)
133
+ dbUrl = envResult.dbUrl;
134
+ console.log(`[nodata-guard] Using database from ${envResult.dbSource}`);
135
+ }
136
+ else {
137
+ // Interactive: ask for consent
138
+ console.log("");
139
+ console.log(" ╔══════════════════════════════════════════╗");
140
+ console.log(" ║ \x1b[33mDatabase connection found\x1b[0m ║");
141
+ console.log(" ╚══════════════════════════════════════════╝");
142
+ console.log(` Source: ${envResult.dbSource}`);
143
+ console.log(` URL: ${masked}`);
144
+ console.log("");
145
+ console.log(" \x1b[2mGuard will connect to your database to verify encryption.\x1b[0m");
146
+ console.log(" \x1b[2mOnly checks if values START WITH encryption prefixes.\x1b[0m");
147
+ console.log(" \x1b[2mNo data values are read, copied, or sent anywhere.\x1b[0m");
148
+ console.log("");
149
+ const consent = await askConsent(" Connect to database? (y/n): ");
150
+ if (consent) {
151
+ dbUrl = envResult.dbUrl;
152
+ console.log(` \x1b[32m✓\x1b[0m Database scan enabled\n`);
153
+ }
154
+ else {
155
+ console.log(" \x1b[33m→\x1b[0m Skipping database scan — code-only mode\n");
156
+ }
157
+ }
158
+ }
159
+ }
117
160
  // Shared Protect config fallback (~/.nodata/config.json)
118
161
  if (!licenseKey) {
119
162
  try {
@@ -583,6 +626,130 @@ function loadOrCreateConfig(projectDir, overrides) {
583
626
  },
584
627
  });
585
628
  }
629
+ // ═══════════════════════════════════════════════════════════
630
+ // Consent & display helpers
631
+ // ═══════════════════════════════════════════════════════════
632
+ function maskDbUrl(url) {
633
+ try {
634
+ const parsed = new URL(url);
635
+ const host = parsed.hostname;
636
+ const port = parsed.port || (url.includes("6543") ? "6543" : "5432");
637
+ const db = parsed.pathname.replace("/", "") || "postgres";
638
+ const user = parsed.username ? parsed.username.slice(0, 8) + "..." : "***";
639
+ return `${user}@${host}:${port}/${db}`;
640
+ }
641
+ catch {
642
+ // Fallback: show first 20 chars + mask
643
+ return url.slice(0, 20) + "...****";
644
+ }
645
+ }
646
+ function askConsent(prompt) {
647
+ return new Promise((resolve) => {
648
+ const readline = require("readline");
649
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
650
+ rl.question(prompt, (answer) => {
651
+ rl.close();
652
+ const a = answer.trim().toLowerCase();
653
+ resolve(a === "y" || a === "yes" || a === "כ" || a === "כן");
654
+ });
655
+ });
656
+ }
657
+ function readEnvFiles(projectDir) {
658
+ const result = {
659
+ dbUrl: null, dbSource: "",
660
+ licenseKey: null, licenseSource: "",
661
+ supabaseUrl: null, supabaseKey: null,
662
+ };
663
+ // Priority order — more specific files override general ones
664
+ const envFiles = [
665
+ ".env",
666
+ ".env.local",
667
+ ".env.development",
668
+ ".env.development.local",
669
+ ".env.production",
670
+ ".env.production.local",
671
+ ];
672
+ // Keys that contain database connection strings
673
+ const DB_KEYS = [
674
+ "DATABASE_URL",
675
+ "DIRECT_URL",
676
+ "POSTGRES_URL",
677
+ "POSTGRES_PRISMA_URL",
678
+ "POSTGRES_URL_NON_POOLING",
679
+ "PG_CONNECTION_STRING",
680
+ "DB_URL",
681
+ "SUPABASE_DB_URL",
682
+ "DATABASE_CONNECTION_STRING",
683
+ ];
684
+ // Keys that contain license/API keys
685
+ const LICENSE_KEYS = [
686
+ "NDC_LICENSE",
687
+ "NODATA_LICENSE_KEY",
688
+ "NODATA_API_KEY",
689
+ "NDC_API_KEY",
690
+ "NDC_PROTECT_KEY",
691
+ "CAPSULE_LICENSE_KEY",
692
+ "CAPSULE_API_KEY",
693
+ ];
694
+ const SUPABASE_URL_KEYS = [
695
+ "NEXT_PUBLIC_SUPABASE_URL",
696
+ "SUPABASE_URL",
697
+ "VITE_SUPABASE_URL",
698
+ "REACT_APP_SUPABASE_URL",
699
+ "NUXT_PUBLIC_SUPABASE_URL",
700
+ ];
701
+ const SUPABASE_KEY_KEYS = [
702
+ "SUPABASE_SERVICE_ROLE_KEY",
703
+ "SUPABASE_SERVICE_KEY",
704
+ ];
705
+ for (const envFile of envFiles) {
706
+ const filePath = (0, path_1.resolve)(projectDir, envFile);
707
+ if (!(0, fs_1.existsSync)(filePath))
708
+ continue;
709
+ try {
710
+ const content = (0, fs_1.readFileSync)(filePath, "utf-8");
711
+ const lines = content.split("\n");
712
+ for (const line of lines) {
713
+ const trimmed = line.trim();
714
+ if (!trimmed || trimmed.startsWith("#"))
715
+ continue;
716
+ const eqIdx = trimmed.indexOf("=");
717
+ if (eqIdx < 1)
718
+ continue;
719
+ const key = trimmed.slice(0, eqIdx).trim();
720
+ let val = trimmed.slice(eqIdx + 1).trim();
721
+ // Strip quotes
722
+ if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
723
+ val = val.slice(1, -1);
724
+ }
725
+ if (!val)
726
+ continue;
727
+ // Check DB keys
728
+ if (!result.dbUrl && DB_KEYS.includes(key)) {
729
+ if (val.startsWith("postgres") || val.startsWith("pg://") || val.includes("5432") || val.includes("6543")) {
730
+ result.dbUrl = val;
731
+ result.dbSource = envFile;
732
+ }
733
+ }
734
+ // Check license keys
735
+ if (!result.licenseKey && LICENSE_KEYS.includes(key)) {
736
+ result.licenseKey = val;
737
+ result.licenseSource = envFile;
738
+ }
739
+ // Check Supabase URL (for fallback DB construction)
740
+ if (!result.supabaseUrl && SUPABASE_URL_KEYS.includes(key)) {
741
+ result.supabaseUrl = val;
742
+ }
743
+ // Check Supabase service role key
744
+ if (!result.supabaseKey && SUPABASE_KEY_KEYS.includes(key)) {
745
+ result.supabaseKey = val;
746
+ }
747
+ }
748
+ }
749
+ catch { /* skip unreadable files */ }
750
+ }
751
+ return result;
752
+ }
586
753
  function handleAttest(args) {
587
754
  let licenseKey;
588
755
  let projectDir = process.cwd();
@@ -715,9 +882,17 @@ function printHelp() {
715
882
  ✓ Prove — cryptographic proof chain of scan results
716
883
  ✓ Monitor — track score over time via dashboard
717
884
 
885
+ Auto-detection:
886
+ Guard automatically reads .env files to find DATABASE_URL
887
+ and license keys. If a database is found, Guard asks for
888
+ your explicit consent before connecting. All values stay
889
+ local — never sent anywhere. In CI mode (--ci), consent
890
+ is automatic (operator configured the environment).
891
+
718
892
  What we NEVER do:
719
893
  ✗ Modify your code, database, or configuration
720
894
  ✗ Receive data values, source code, or credentials
895
+ ✗ Read actual data from your database (only checks prefixes)
721
896
 
722
897
  Important:
723
898
  Recommendations require understanding of YOUR system —
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodatachat/guard",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "NoData Guard — continuous security scanner. Runs locally, reports only metadata. Your data never leaves your machine.",
5
5
  "main": "./dist/cli.js",
6
6
  "types": "./dist/cli.d.ts",