@datasynx/agentic-ai-cartography 2.10.0 → 2.12.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.
@@ -20,12 +20,13 @@ import {
20
20
  k8sScanner,
21
21
  keyMetaOf,
22
22
  normalizeTenant,
23
+ redactSecrets,
23
24
  redactValue,
24
25
  resolvePrincipal,
25
26
  sanitizeUntrusted,
26
27
  stableStringify,
27
28
  stripSensitive
28
- } from "./chunk-YVV6NIT2.js";
29
+ } from "./chunk-LO6YFS6H.js";
29
30
  import {
30
31
  EdgeSchema,
31
32
  NODE_TYPES,
@@ -653,9 +654,132 @@ var serviceConfigScanner = {
653
654
  }
654
655
  };
655
656
 
657
+ // src/scanners/terraform.ts
658
+ import { readFileSync } from "fs";
659
+ var TYPE_RULES = [
660
+ [/(db_instance|_rds|sql_database|sql_instance|database_instance|cosmosdb|dynamodb|spanner|bigtable|documentdb|redshift)/, "database_server"],
661
+ [/(elasticache|_redis|memcached|memorystore)/, "cache_server"],
662
+ [/(s3_bucket|storage_bucket|gcs_bucket|storage_account|_blob)/, "database"],
663
+ [/(_sqs|_queue|servicebus_queue)/, "queue"],
664
+ [/(_sns|_topic|pubsub_topic|servicebus_topic)/, "topic"],
665
+ [/(kafka|_msk|event_hub|kinesis)/, "message_broker"],
666
+ [/(_eks|_gke|_aks|kubernetes_cluster|container_cluster)/, "k8s_cluster"],
667
+ [/(ecs_|_container|fargate)/, "container"],
668
+ [/(lambda|cloud_function|cloudfunctions|function_app|cloud_run)/, "web_service"],
669
+ [/(_lb$|load_balancer|_alb|_elb|application_gateway)/, "web_service"],
670
+ [/(api_gateway|apigateway)/, "api_endpoint"],
671
+ [/(_instance|virtual_machine|_vm$|compute_instance)/, "host"]
672
+ ];
673
+ function terraformTypeToNode(tfType) {
674
+ const t = tfType.toLowerCase();
675
+ for (const [re, nt] of TYPE_RULES) if (re.test(t)) return nt;
676
+ return "unknown";
677
+ }
678
+ var IDENTITY_ATTRS = ["id", "arn", "region", "location", "instance_type", "engine", "machine_type"];
679
+ var OWNER_TAGS = ["Owner", "owner", "Team", "team"];
680
+ var SAFE_TAG_KEYS = /* @__PURE__ */ new Set(["Name", "name", "Owner", "owner", "Team", "team", "Env", "env", "Environment", "environment", "Service", "service", "Component", "component", "App", "app", "Project", "project", "Tier", "tier", "Role", "role"]);
681
+ var SECRET_KEY = /pass|secret|token|key|pwd|cred|private/i;
682
+ function attrTags(tags) {
683
+ if (!tags || typeof tags !== "object") return [];
684
+ return Object.entries(tags).filter(([k]) => SAFE_TAG_KEYS.has(k) && !SECRET_KEY.test(k)).map(([k, v]) => `${k}:${redactSecrets(String(v))}`);
685
+ }
686
+ function parseTerraformState(json2) {
687
+ let state;
688
+ try {
689
+ state = JSON.parse(json2);
690
+ } catch {
691
+ return { nodes: [], edges: [] };
692
+ }
693
+ const resources = Array.isArray(state?.resources) ? state.resources : [];
694
+ const nodes = [];
695
+ const edges = [];
696
+ const addrToId = /* @__PURE__ */ new Map();
697
+ for (const raw of resources) {
698
+ const r = raw;
699
+ if (r.mode && r.mode !== "managed") continue;
700
+ if (typeof r.type !== "string" || typeof r.name !== "string") continue;
701
+ const address = `${r.type}.${r.name}`;
702
+ const nt = terraformTypeToNode(r.type);
703
+ const id = `${nt}:terraform:${address}`;
704
+ if (addrToId.has(address)) continue;
705
+ addrToId.set(address, id);
706
+ const inst = Array.isArray(r.instances) ? r.instances[0] : void 0;
707
+ const attrs = inst?.attributes ?? {};
708
+ const identity = { source: "terraform", tfType: r.type };
709
+ for (const k of IDENTITY_ATTRS) if (attrs[k] !== void 0) identity[k] = attrs[k];
710
+ const owner = OWNER_TAGS.map((k) => attrs.tags?.[k]).find((v) => typeof v === "string");
711
+ nodes.push({
712
+ id,
713
+ type: nt,
714
+ name: address,
715
+ discoveredVia: "terraform-state",
716
+ confidence: 0.9,
717
+ // IaC is authoritative declared intent.
718
+ metadata: redactValue(identity),
719
+ tags: attrTags(attrs.tags),
720
+ ...owner ? { owner } : {}
721
+ });
722
+ }
723
+ for (const raw of resources) {
724
+ const r = raw;
725
+ if (r.mode && r.mode !== "managed") continue;
726
+ if (typeof r.type !== "string" || typeof r.name !== "string") continue;
727
+ const srcId = addrToId.get(`${r.type}.${r.name}`);
728
+ if (!srcId) continue;
729
+ const inst = Array.isArray(r.instances) ? r.instances[0] : void 0;
730
+ const deps = Array.isArray(inst?.dependencies) ? inst.dependencies : [];
731
+ for (const dep of deps) {
732
+ if (typeof dep !== "string") continue;
733
+ const tgtId = addrToId.get(dep) ?? addrToId.get(dep.split("[")[0]);
734
+ if (!tgtId || tgtId === srcId) continue;
735
+ edges.push({ sourceId: srcId, targetId: tgtId, relationship: "depends_on", evidence: evidenceLine("config-declared", `terraform depends_on ${dep}`), confidence: 0.85 });
736
+ }
737
+ }
738
+ return { nodes, edges };
739
+ }
740
+ function stateDirs() {
741
+ return [".", "./terraform", "./infra", "./infrastructure", "./deploy", "./terraform/environments"];
742
+ }
743
+ function hintPath(hint) {
744
+ if (!hint) return void 0;
745
+ const m = /(?:^|[\s,])tfstate=([^\s,]+)/.exec(hint);
746
+ return m ? m[1] : void 0;
747
+ }
748
+ function resolveStatePath(ctx) {
749
+ const explicit = hintPath(ctx.hint);
750
+ if (explicit) return explicit;
751
+ const found = (ctx.findFiles ?? findFiles)(stateDirs(), ["*.tfstate"], 4, 20).split(/\r?\n/).map((s) => s.trim()).filter(Boolean);
752
+ return found[0];
753
+ }
754
+ function readStateFile(path) {
755
+ try {
756
+ return readFileSync(path, "utf8");
757
+ } catch {
758
+ return "";
759
+ }
760
+ }
761
+ var terraformScanner = {
762
+ id: "terraform-state",
763
+ title: "Terraform state (IaC)",
764
+ platforms: "all",
765
+ // No shell commands — the state file is read directly via node:fs, so an
766
+ // operator-supplied path can never inject a command (no `cat "${path}"` interpolation).
767
+ detect(ctx) {
768
+ return resolveStatePath(ctx) !== void 0;
769
+ },
770
+ async scan(ctx) {
771
+ const path = resolveStatePath(ctx);
772
+ if (!path) return { nodes: [], edges: [] };
773
+ const json2 = (ctx.readFile ?? readStateFile)(path);
774
+ if (!json2) return { nodes: [], edges: [] };
775
+ const result = parseTerraformState(json2);
776
+ return { ...result, report: `terraform-state: ${result.nodes.length} resources, ${result.edges.length} dependencies from ${path}` };
777
+ }
778
+ };
779
+
656
780
  // src/scanners/registry.ts
657
781
  function defaultRegistry() {
658
- return new ScannerRegistry().register(bookmarksScanner).register(installedAppsScanner).register(portsScanner).register(cloudAwsScanner).register(cloudGcpScanner).register(cloudAzureScanner).register(k8sScanner).register(databasesScanner).register(connectionsScanner).register(serviceConfigScanner);
782
+ return new ScannerRegistry().register(bookmarksScanner).register(installedAppsScanner).register(portsScanner).register(cloudAwsScanner).register(cloudGcpScanner).register(cloudAzureScanner).register(k8sScanner).register(databasesScanner).register(connectionsScanner).register(serviceConfigScanner).register(terraformScanner);
659
783
  }
660
784
 
661
785
  // src/scanners/loader.ts
@@ -764,201 +888,6 @@ function localDiscoveryFn(registry, plugins) {
764
888
  };
765
889
  }
766
890
 
767
- // src/compliance/rulesets/baseline.ts
768
- var baseline = RulesetSchema.parse({
769
- name: "baseline",
770
- version: "1.0.0",
771
- framework: "baseline",
772
- description: "Deterministic baseline hygiene controls scored against signals available today.",
773
- rules: [
774
- {
775
- id: "BASE-1",
776
- control: "BASE-1",
777
- framework: "baseline",
778
- severity: "medium",
779
- title: "Asset has an owner",
780
- rationale: "Every asset should have a clear owning team/person for accountability.",
781
- scope: {},
782
- check: { any: [
783
- { field: "owner", op: "present" },
784
- { field: "domain", op: "present" },
785
- { field: "tags", op: "matches", pattern: "owner_key" },
786
- { field: "metadataKeys", op: "matches", pattern: "owner_key" }
787
- ] }
788
- },
789
- {
790
- id: "BASE-2",
791
- control: "BASE-2",
792
- framework: "baseline",
793
- severity: "low",
794
- title: "Service/data asset is assigned a business domain",
795
- rationale: "Domain assignment enables blast-radius and ownership analysis.",
796
- scope: { groups: ["data", "web"] },
797
- check: { field: "domain", op: "present" }
798
- },
799
- {
800
- id: "BASE-3",
801
- control: "BASE-3",
802
- framework: "baseline",
803
- severity: "high",
804
- title: "Critical asset is discovered with adequate confidence",
805
- rationale: "Low-confidence critical assets indicate incomplete or unreliable discovery.",
806
- scope: { groups: ["data", "infra"] },
807
- check: { field: "confidence", op: "gte", value: 0.5 }
808
- },
809
- {
810
- id: "BASE-4",
811
- control: "BASE-4",
812
- framework: "baseline",
813
- severity: "critical",
814
- title: "No embedded credentials / plaintext DSN in metadata",
815
- rationale: "A connection string with embedded credentials is a direct secret-exposure risk.",
816
- scope: {},
817
- check: { not: { field: "metadataValues", op: "matches", pattern: "dsn_with_credentials" } }
818
- },
819
- {
820
- id: "BASE-5",
821
- control: "BASE-5",
822
- framework: "baseline",
823
- severity: "medium",
824
- title: "Data store carries an acceptable quality score",
825
- rationale: "Where a quality score exists, a low score flags an under-governed data store.",
826
- scope: { groups: ["data"] },
827
- applicableWhen: { field: "qualityScore", op: "present" },
828
- check: { field: "qualityScore", op: "gte", value: 50 }
829
- }
830
- ]
831
- });
832
-
833
- // src/compliance/rulesets/cis.ts
834
- var cis = RulesetSchema.parse({
835
- name: "cis",
836
- version: "0.1.0",
837
- framework: "CIS",
838
- description: "CIS starter subset \u2014 illustrative controls, not a certified benchmark.",
839
- rules: [
840
- {
841
- id: "CIS-1.1",
842
- control: "CIS-1.1",
843
- framework: "CIS",
844
- severity: "critical",
845
- title: "No plaintext credentials in service configuration",
846
- rationale: "CIS hardening forbids embedded secrets in config/metadata.",
847
- scope: {},
848
- check: { not: { field: "metadataValues", op: "matches", pattern: "dsn_with_credentials" } }
849
- },
850
- {
851
- id: "CIS-2.1",
852
- control: "CIS-2.1",
853
- framework: "CIS",
854
- severity: "high",
855
- title: "Data store is not publicly exposed",
856
- rationale: "Data stores should not bind to public/0.0.0.0 addresses.",
857
- scope: { groups: ["data"] },
858
- check: { not: { field: "metadataValues", op: "matches", pattern: "public_exposure" } }
859
- },
860
- {
861
- id: "CIS-3.1",
862
- control: "CIS-3.1",
863
- framework: "CIS",
864
- severity: "medium",
865
- title: "Asset inventory has an accountable owner",
866
- rationale: "CIS asset management requires an assigned owner.",
867
- scope: {},
868
- check: { any: [{ field: "owner", op: "present" }, { field: "metadataKeys", op: "matches", pattern: "owner_key" }] }
869
- }
870
- ]
871
- });
872
-
873
- // src/compliance/rulesets/soc2.ts
874
- var soc2 = RulesetSchema.parse({
875
- name: "soc2",
876
- version: "0.1.0",
877
- framework: "SOC2",
878
- description: "SOC 2 starter subset \u2014 illustrative controls, not a certified control set.",
879
- rules: [
880
- {
881
- id: "CC6.1",
882
- control: "CC6.1",
883
- framework: "SOC2",
884
- severity: "critical",
885
- title: "Logical access \u2014 no embedded credentials",
886
- rationale: "CC6.1 logical access controls preclude plaintext secrets in config.",
887
- scope: {},
888
- check: { not: { field: "metadataValues", op: "matches", pattern: "dsn_with_credentials" } }
889
- },
890
- {
891
- id: "CC6.6",
892
- control: "CC6.6",
893
- framework: "SOC2",
894
- severity: "high",
895
- title: "Boundary protection \u2014 data stores not public",
896
- rationale: "CC6.6 requires protection of system boundaries from external access.",
897
- scope: { groups: ["data"] },
898
- check: { not: { field: "metadataValues", op: "matches", pattern: "public_exposure" } }
899
- },
900
- {
901
- id: "CC1.2",
902
- control: "CC1.2",
903
- framework: "SOC2",
904
- severity: "medium",
905
- title: "Accountability \u2014 asset has an owner",
906
- rationale: "CC1.2 establishes accountability; assets need an assigned owner.",
907
- scope: {},
908
- check: { any: [{ field: "owner", op: "present" }, { field: "domain", op: "present" }] }
909
- }
910
- ]
911
- });
912
-
913
- // src/compliance/rulesets/iso27001.ts
914
- var iso27001 = RulesetSchema.parse({
915
- name: "iso27001",
916
- version: "0.1.0",
917
- framework: "ISO27001",
918
- description: "ISO/IEC 27001 Annex A starter subset \u2014 illustrative, not certified.",
919
- rules: [
920
- {
921
- id: "A.8.1",
922
- control: "A.8.1",
923
- framework: "ISO27001",
924
- severity: "medium",
925
- title: "Inventory of assets \u2014 ownership assigned",
926
- rationale: "A.8.1 requires an inventory of assets each with an identified owner.",
927
- scope: {},
928
- check: { any: [{ field: "owner", op: "present" }, { field: "metadataKeys", op: "matches", pattern: "owner_key" }] }
929
- },
930
- {
931
- id: "A.8.12",
932
- control: "A.8.12",
933
- framework: "ISO27001",
934
- severity: "critical",
935
- title: "Data leakage prevention \u2014 no embedded secrets",
936
- rationale: "A.8.12 mandates measures against data leakage such as exposed credentials.",
937
- scope: {},
938
- check: { not: { field: "metadataValues", op: "matches", pattern: "dsn_with_credentials" } }
939
- },
940
- {
941
- id: "A.8.20",
942
- control: "A.8.20",
943
- framework: "ISO27001",
944
- severity: "high",
945
- title: "Network security \u2014 data stores not publicly exposed",
946
- rationale: "A.8.20 requires networks to be secured; data stores should not be public.",
947
- scope: { groups: ["data"] },
948
- check: { not: { field: "metadataValues", op: "matches", pattern: "public_exposure" } }
949
- }
950
- ]
951
- });
952
-
953
- // src/compliance/rulesets/registry.ts
954
- var RULESETS = { baseline, cis, soc2, iso27001 };
955
- function getRuleset(name) {
956
- return RULESETS[name];
957
- }
958
- function listRulesets() {
959
- return Object.values(RULESETS).map((r) => ({ name: r.name, version: r.version, framework: r.framework, ruleCount: r.rules.length }));
960
- }
961
-
962
891
  // src/sinks/stdout.ts
963
892
  var StdoutSink = class {
964
893
  name = "stdout";
@@ -1315,9 +1244,204 @@ async function runDrift(db, config, opts = {}) {
1315
1244
  return alert;
1316
1245
  }
1317
1246
 
1247
+ // src/compliance/rulesets/baseline.ts
1248
+ var baseline = RulesetSchema.parse({
1249
+ name: "baseline",
1250
+ version: "1.0.0",
1251
+ framework: "baseline",
1252
+ description: "Deterministic baseline hygiene controls scored against signals available today.",
1253
+ rules: [
1254
+ {
1255
+ id: "BASE-1",
1256
+ control: "BASE-1",
1257
+ framework: "baseline",
1258
+ severity: "medium",
1259
+ title: "Asset has an owner",
1260
+ rationale: "Every asset should have a clear owning team/person for accountability.",
1261
+ scope: {},
1262
+ check: { any: [
1263
+ { field: "owner", op: "present" },
1264
+ { field: "domain", op: "present" },
1265
+ { field: "tags", op: "matches", pattern: "owner_key" },
1266
+ { field: "metadataKeys", op: "matches", pattern: "owner_key" }
1267
+ ] }
1268
+ },
1269
+ {
1270
+ id: "BASE-2",
1271
+ control: "BASE-2",
1272
+ framework: "baseline",
1273
+ severity: "low",
1274
+ title: "Service/data asset is assigned a business domain",
1275
+ rationale: "Domain assignment enables blast-radius and ownership analysis.",
1276
+ scope: { groups: ["data", "web"] },
1277
+ check: { field: "domain", op: "present" }
1278
+ },
1279
+ {
1280
+ id: "BASE-3",
1281
+ control: "BASE-3",
1282
+ framework: "baseline",
1283
+ severity: "high",
1284
+ title: "Critical asset is discovered with adequate confidence",
1285
+ rationale: "Low-confidence critical assets indicate incomplete or unreliable discovery.",
1286
+ scope: { groups: ["data", "infra"] },
1287
+ check: { field: "confidence", op: "gte", value: 0.5 }
1288
+ },
1289
+ {
1290
+ id: "BASE-4",
1291
+ control: "BASE-4",
1292
+ framework: "baseline",
1293
+ severity: "critical",
1294
+ title: "No embedded credentials / plaintext DSN in metadata",
1295
+ rationale: "A connection string with embedded credentials is a direct secret-exposure risk.",
1296
+ scope: {},
1297
+ check: { not: { field: "metadataValues", op: "matches", pattern: "dsn_with_credentials" } }
1298
+ },
1299
+ {
1300
+ id: "BASE-5",
1301
+ control: "BASE-5",
1302
+ framework: "baseline",
1303
+ severity: "medium",
1304
+ title: "Data store carries an acceptable quality score",
1305
+ rationale: "Where a quality score exists, a low score flags an under-governed data store.",
1306
+ scope: { groups: ["data"] },
1307
+ applicableWhen: { field: "qualityScore", op: "present" },
1308
+ check: { field: "qualityScore", op: "gte", value: 50 }
1309
+ }
1310
+ ]
1311
+ });
1312
+
1313
+ // src/compliance/rulesets/cis.ts
1314
+ var cis = RulesetSchema.parse({
1315
+ name: "cis",
1316
+ version: "0.1.0",
1317
+ framework: "CIS",
1318
+ description: "CIS starter subset \u2014 illustrative controls, not a certified benchmark.",
1319
+ rules: [
1320
+ {
1321
+ id: "CIS-1.1",
1322
+ control: "CIS-1.1",
1323
+ framework: "CIS",
1324
+ severity: "critical",
1325
+ title: "No plaintext credentials in service configuration",
1326
+ rationale: "CIS hardening forbids embedded secrets in config/metadata.",
1327
+ scope: {},
1328
+ check: { not: { field: "metadataValues", op: "matches", pattern: "dsn_with_credentials" } }
1329
+ },
1330
+ {
1331
+ id: "CIS-2.1",
1332
+ control: "CIS-2.1",
1333
+ framework: "CIS",
1334
+ severity: "high",
1335
+ title: "Data store is not publicly exposed",
1336
+ rationale: "Data stores should not bind to public/0.0.0.0 addresses.",
1337
+ scope: { groups: ["data"] },
1338
+ check: { not: { field: "metadataValues", op: "matches", pattern: "public_exposure" } }
1339
+ },
1340
+ {
1341
+ id: "CIS-3.1",
1342
+ control: "CIS-3.1",
1343
+ framework: "CIS",
1344
+ severity: "medium",
1345
+ title: "Asset inventory has an accountable owner",
1346
+ rationale: "CIS asset management requires an assigned owner.",
1347
+ scope: {},
1348
+ check: { any: [{ field: "owner", op: "present" }, { field: "metadataKeys", op: "matches", pattern: "owner_key" }] }
1349
+ }
1350
+ ]
1351
+ });
1352
+
1353
+ // src/compliance/rulesets/soc2.ts
1354
+ var soc2 = RulesetSchema.parse({
1355
+ name: "soc2",
1356
+ version: "0.1.0",
1357
+ framework: "SOC2",
1358
+ description: "SOC 2 starter subset \u2014 illustrative controls, not a certified control set.",
1359
+ rules: [
1360
+ {
1361
+ id: "CC6.1",
1362
+ control: "CC6.1",
1363
+ framework: "SOC2",
1364
+ severity: "critical",
1365
+ title: "Logical access \u2014 no embedded credentials",
1366
+ rationale: "CC6.1 logical access controls preclude plaintext secrets in config.",
1367
+ scope: {},
1368
+ check: { not: { field: "metadataValues", op: "matches", pattern: "dsn_with_credentials" } }
1369
+ },
1370
+ {
1371
+ id: "CC6.6",
1372
+ control: "CC6.6",
1373
+ framework: "SOC2",
1374
+ severity: "high",
1375
+ title: "Boundary protection \u2014 data stores not public",
1376
+ rationale: "CC6.6 requires protection of system boundaries from external access.",
1377
+ scope: { groups: ["data"] },
1378
+ check: { not: { field: "metadataValues", op: "matches", pattern: "public_exposure" } }
1379
+ },
1380
+ {
1381
+ id: "CC1.2",
1382
+ control: "CC1.2",
1383
+ framework: "SOC2",
1384
+ severity: "medium",
1385
+ title: "Accountability \u2014 asset has an owner",
1386
+ rationale: "CC1.2 establishes accountability; assets need an assigned owner.",
1387
+ scope: {},
1388
+ check: { any: [{ field: "owner", op: "present" }, { field: "domain", op: "present" }] }
1389
+ }
1390
+ ]
1391
+ });
1392
+
1393
+ // src/compliance/rulesets/iso27001.ts
1394
+ var iso27001 = RulesetSchema.parse({
1395
+ name: "iso27001",
1396
+ version: "0.1.0",
1397
+ framework: "ISO27001",
1398
+ description: "ISO/IEC 27001 Annex A starter subset \u2014 illustrative, not certified.",
1399
+ rules: [
1400
+ {
1401
+ id: "A.8.1",
1402
+ control: "A.8.1",
1403
+ framework: "ISO27001",
1404
+ severity: "medium",
1405
+ title: "Inventory of assets \u2014 ownership assigned",
1406
+ rationale: "A.8.1 requires an inventory of assets each with an identified owner.",
1407
+ scope: {},
1408
+ check: { any: [{ field: "owner", op: "present" }, { field: "metadataKeys", op: "matches", pattern: "owner_key" }] }
1409
+ },
1410
+ {
1411
+ id: "A.8.12",
1412
+ control: "A.8.12",
1413
+ framework: "ISO27001",
1414
+ severity: "critical",
1415
+ title: "Data leakage prevention \u2014 no embedded secrets",
1416
+ rationale: "A.8.12 mandates measures against data leakage such as exposed credentials.",
1417
+ scope: {},
1418
+ check: { not: { field: "metadataValues", op: "matches", pattern: "dsn_with_credentials" } }
1419
+ },
1420
+ {
1421
+ id: "A.8.20",
1422
+ control: "A.8.20",
1423
+ framework: "ISO27001",
1424
+ severity: "high",
1425
+ title: "Network security \u2014 data stores not publicly exposed",
1426
+ rationale: "A.8.20 requires networks to be secured; data stores should not be public.",
1427
+ scope: { groups: ["data"] },
1428
+ check: { not: { field: "metadataValues", op: "matches", pattern: "public_exposure" } }
1429
+ }
1430
+ ]
1431
+ });
1432
+
1433
+ // src/compliance/rulesets/registry.ts
1434
+ var RULESETS = { baseline, cis, soc2, iso27001 };
1435
+ function getRuleset(name) {
1436
+ return RULESETS[name];
1437
+ }
1438
+ function listRulesets() {
1439
+ return Object.values(RULESETS).map((r) => ({ name: r.name, version: r.version, framework: r.framework, ruleCount: r.rules.length }));
1440
+ }
1441
+
1318
1442
  // src/orgkey.ts
1319
1443
  import { randomBytes, hkdfSync } from "crypto";
1320
- import { existsSync, mkdirSync, readFileSync, writeFileSync, statSync } from "fs";
1444
+ import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync, statSync } from "fs";
1321
1445
  import { homedir } from "os";
1322
1446
  import { dirname, join } from "path";
1323
1447
  function orgKeyPath(home = homedir()) {
@@ -1333,7 +1457,7 @@ function loadFileSecret(path) {
1333
1457
  }
1334
1458
  } catch {
1335
1459
  }
1336
- const hex = readFileSync(path, "utf8").trim();
1460
+ const hex = readFileSync2(path, "utf8").trim();
1337
1461
  const buf = Buffer.from(hex, "hex");
1338
1462
  if (buf.length === KEY_BYTES) return buf;
1339
1463
  logWarn("org-key file was malformed \u2014 regenerating a fresh org key");
@@ -1370,6 +1494,8 @@ function reversalKey(orgKey) {
1370
1494
  import { createHmac, createCipheriv, createDecipheriv, randomBytes as randomBytes2 } from "crypto";
1371
1495
  var PRIVATE_IP = /\b(?:10(?:\.\d{1,3}){3}|192\.168(?:\.\d{1,3}){2}|172\.(?:1[6-9]|2\d|3[01])(?:\.\d{1,3}){2})\b/g;
1372
1496
  var HOSTNAME = /\b(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}\b/gi;
1497
+ var BARE_INTERNAL_HOST = /^[a-z0-9]+(?:-[a-z0-9]+)+$|^[a-z]+\d+$|^\d+[a-z]+$/i;
1498
+ var ANON_TOKEN = /^anon:(?:host|user|path|ip):[a-z2-7]+$/;
1373
1499
  var POSIX_PATH = /(?:^|(?<=\s|=|:|"|'|\())(\/[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)+)/g;
1374
1500
  var WIN_PATH = /\b[A-Za-z]:\\[A-Za-z0-9._\\-]+/g;
1375
1501
  var B32_ALPHABET = "abcdefghijklmnopqrstuvwxyz234567";
@@ -1424,8 +1550,18 @@ function pseudonymizeString(s, orgKey, db) {
1424
1550
  (_m, user, host) => `${pseudonymizeFragment(user, "user", orgKey, db)}@${pseudonymizeFragment(host, "host", orgKey, db)}`
1425
1551
  );
1426
1552
  out = out.replace(HOSTNAME, (m) => pseudonymizeFragment(m, "host", orgKey, db));
1553
+ const trimmed = out.trim();
1554
+ if (out === s && !ANON_TOKEN.test(trimmed) && BARE_INTERNAL_HOST.test(trimmed)) {
1555
+ out = pseudonymizeFragment(trimmed, "host", orgKey, db);
1556
+ }
1427
1557
  return out;
1428
1558
  }
1559
+ function pseudonymizeId(id, orgKey, db) {
1560
+ const segments = id.split(":");
1561
+ if (segments.length <= 1) return pseudonymizeString(id, orgKey, db);
1562
+ const [type, ...rest] = segments;
1563
+ return [type, ...rest.map((seg) => pseudonymizeString(seg, orgKey, db))].join(":");
1564
+ }
1429
1565
  function pseudonymize(value, orgKey, db) {
1430
1566
  if (typeof value === "string") return pseudonymizeString(value, orgKey, db);
1431
1567
  if (Array.isArray(value)) return value.map((v) => pseudonymize(v, orgKey, db));
@@ -1676,7 +1812,7 @@ function correlateTopology(nodes, _edges = []) {
1676
1812
 
1677
1813
  // src/mcp/server.ts
1678
1814
  var SERVER_NAME = "cartography";
1679
- var SERVER_VERSION = "2.10.0";
1815
+ var SERVER_VERSION = "2.12.1";
1680
1816
  var SERVICE_TYPES = NODE_TYPE_GROUPS.web;
1681
1817
  var DATA_TYPES = NODE_TYPE_GROUPS.data;
1682
1818
  var lexicalSearch = async (db, sessionId, query, opts) => db.searchNodes(sessionId, query, { types: opts.types, limit: opts.limit }).map((node) => ({ node }));
@@ -2688,8 +2824,6 @@ var FQDN = /\b(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,}\b/gi;
2688
2824
  var POSIX_PATH2 = /(?:^|(?<=\s|=|:|"|'|\())(\/[A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)+)/g;
2689
2825
  var WIN_PATH2 = /\b[A-Za-z]:\\[A-Za-z0-9._\\-]+/g;
2690
2826
  var HOME_USER = /(?:\/home\/|\/Users\/|[A-Za-z]:\\Users\\)([A-Za-z0-9._-]+)/g;
2691
- var BARE_INTERNAL_HOST = /^[a-z0-9]+(?:-[a-z0-9]+)+$|^[a-z]+\d+$|^\d+[a-z]+$/i;
2692
- var ANON_TOKEN = /^anon:(?:host|user|path|ip):[a-z2-7]+$/;
2693
2827
  function violationsInString(s, path) {
2694
2828
  const out = [];
2695
2829
  const trimmed = s.trim();
@@ -3015,17 +3149,19 @@ async function startMcp(opts = {}) {
3015
3149
  }
3016
3150
 
3017
3151
  export {
3152
+ ScannerRegistry,
3018
3153
  isPersonalHost,
3019
3154
  runLocalDiscovery,
3155
+ runDrift,
3020
3156
  getRuleset,
3021
3157
  listRulesets,
3022
- runDrift,
3023
3158
  loadOrgKey,
3024
3159
  rotateOrgKey,
3025
3160
  pseudonymizeString,
3161
+ pseudonymizeId,
3026
3162
  pseudonymize,
3027
3163
  reversePseudonym,
3028
3164
  parseMcpArgs,
3029
3165
  startMcp
3030
3166
  };
3031
- //# sourceMappingURL=chunk-ASCA3UFM.js.map
3167
+ //# sourceMappingURL=chunk-OIDAXUW5.js.map