secufusion-mcp 1.0.21 → 1.0.23
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.
- package/.secufusion-project-spec.json +242 -34
- package/index.js +56 -24
- package/package.json +1 -1
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
"generated_from": "codebase analysis",
|
|
5
5
|
"workspace_root": "c:\\Users\\Yash\\Desktop\\secufi_full"
|
|
6
6
|
},
|
|
7
|
-
|
|
8
7
|
"project": {
|
|
9
8
|
"name": "SecuFusion",
|
|
10
9
|
"devops_org": "secufusion",
|
|
@@ -13,14 +12,21 @@
|
|
|
13
12
|
"branch_naming_convention": "Mixed, not strictly enforced. Common patterns: feature/<kebab-description>, fix/<desc> or bugfix/<ticket-or-desc>, ticket-only (TASK-####, TASK-####-<desc>, BUG-####-<desc>), release-<major>.<minor>.<patch> (dash, not slash) for release branches. Trunk branches are 'main' and 'develop' in every repo, but which one is checked out as the working default varies per repo (sfn-events-api and sfn-web-ui default to develop; snf-browser-extn defaults to main). Many ad hoc/free-text branch names also exist.",
|
|
14
13
|
"commit_message_pattern": "No single enforced convention. Recurring mixture: (1) Azure Repos auto-generated 'Merged PR <number>: <title>' on PR completion (dominant pattern in all 3 sampled repos), (2) ticket-prefixed 'TASK-<number>: <desc>' or 'TASK-<number>-<desc>' (common in sfn-events-api), (3) conventional-commit style 'feat(<scope>): ...' / 'fix(<scope>): ...' (used consistently within recent 'ai-ops' feature work in sfn-web-ui, not elsewhere), (4) plain imperative descriptive messages with no prefix at all. Work items referenced via https://dev.azure.com/secufusion/SFCloud-MSSP/_workitems/edit/<id>."
|
|
15
14
|
},
|
|
16
|
-
|
|
17
15
|
"microservices": {
|
|
18
16
|
"sfn-auth-api": {
|
|
19
17
|
"artifact_id": "sfn-auth-api",
|
|
20
18
|
"port": "CONFLICT: 8081 (hardcoded literal in application.properties, confirmed by direct read) vs 8087 (AUTH_SERVICE_PORT in shared deployment env config supplied by user) — the repo's server.port is NOT wired to ${AUTH_SERVICE_PORT}, so 8087 appears unused by this service as currently checked out; flagged for manual verification",
|
|
21
19
|
"eureka_name": "AUTH-SERVICE",
|
|
22
20
|
"domain": "Legacy/parallel auth & tenant-config resolution (login, tenant-config lookup by host). Heavy functional overlap with sfn-iam-api's AuthController (see manual_review_flags) — possibly superseded/duplicate.",
|
|
23
|
-
"owns_tables": [
|
|
21
|
+
"owns_tables": [
|
|
22
|
+
"Address",
|
|
23
|
+
"auth_provider_config",
|
|
24
|
+
"groups",
|
|
25
|
+
"roles",
|
|
26
|
+
"scopes",
|
|
27
|
+
"Tenant",
|
|
28
|
+
"Users"
|
|
29
|
+
],
|
|
24
30
|
"exposes_endpoints": [
|
|
25
31
|
"GET /tenant-config/v1 -> AuthController.getTenantConfig() [host param, referer-validated]",
|
|
26
32
|
"GET /tenant-config -> AuthController.getTenantConfig() [host param, unvalidated overload]",
|
|
@@ -37,7 +43,6 @@
|
|
|
37
43
|
"spring_boot_version": "3.4.12",
|
|
38
44
|
"java_version": "17"
|
|
39
45
|
},
|
|
40
|
-
|
|
41
46
|
"sfn-eureka-api": {
|
|
42
47
|
"artifact_id": "sfn-eureka-api",
|
|
43
48
|
"port": 8761,
|
|
@@ -56,13 +61,64 @@
|
|
|
56
61
|
"spring_boot_version": "3.2.4",
|
|
57
62
|
"java_version": "17"
|
|
58
63
|
},
|
|
59
|
-
|
|
60
64
|
"sfn-events-api": {
|
|
61
65
|
"artifact_id": "sfn-events-api",
|
|
62
66
|
"port": 8086,
|
|
63
67
|
"eureka_name": "EVENTS-SERVICE",
|
|
64
68
|
"domain": "Security event ingestion, incident management, device/extension inventory, AI-on-endpoints governance, notifications. Largest/most active service (22 controllers).",
|
|
65
|
-
"owns_tables": [
|
|
69
|
+
"owns_tables": [
|
|
70
|
+
"devices",
|
|
71
|
+
"device_user",
|
|
72
|
+
"device_credential",
|
|
73
|
+
"events",
|
|
74
|
+
"event_inbox",
|
|
75
|
+
"extension_events",
|
|
76
|
+
"extension_api_keys",
|
|
77
|
+
"extension_approval_request",
|
|
78
|
+
"extension_policy",
|
|
79
|
+
"installed_extensions",
|
|
80
|
+
"events_group",
|
|
81
|
+
"incidents",
|
|
82
|
+
"incident_activities",
|
|
83
|
+
"incident_assignees",
|
|
84
|
+
"incident_events",
|
|
85
|
+
"incident_playbooks",
|
|
86
|
+
"incident_playbook_steps",
|
|
87
|
+
"playbook_templates",
|
|
88
|
+
"escalation_rules",
|
|
89
|
+
"machine",
|
|
90
|
+
"groups",
|
|
91
|
+
"notifications",
|
|
92
|
+
"notification_preferences",
|
|
93
|
+
"smtp_config",
|
|
94
|
+
"policy_assignments",
|
|
95
|
+
"tenant_api_mappings",
|
|
96
|
+
"tenant_extension_client",
|
|
97
|
+
"tenant_extension_first_seen",
|
|
98
|
+
"ai_policy",
|
|
99
|
+
"ai_device_scan",
|
|
100
|
+
"ai_host_instance",
|
|
101
|
+
"ai_mcp_server_instance",
|
|
102
|
+
"ai_local_model",
|
|
103
|
+
"ai_instruction_file",
|
|
104
|
+
"ai_extensibility_item",
|
|
105
|
+
"ai_versioned_record",
|
|
106
|
+
"browserpolicy",
|
|
107
|
+
"networkpolicy",
|
|
108
|
+
"Users",
|
|
109
|
+
"roles",
|
|
110
|
+
"auth_provider_config",
|
|
111
|
+
"Tenant",
|
|
112
|
+
"api_flags",
|
|
113
|
+
"downloadable_files",
|
|
114
|
+
"extension_registry",
|
|
115
|
+
"extension_version",
|
|
116
|
+
"extension_risk_components",
|
|
117
|
+
"permission_risk_classification",
|
|
118
|
+
"api_key_configuration",
|
|
119
|
+
"scopes",
|
|
120
|
+
"tenant_types"
|
|
121
|
+
],
|
|
66
122
|
"exposes_endpoints": [
|
|
67
123
|
"GET /api/events/reports/ai-usage/summary -> AiUsageReportController.summary()",
|
|
68
124
|
"GET /api/events/reports/ai-usage/by-platform -> AiUsageReportController.byPlatform()",
|
|
@@ -246,8 +302,16 @@
|
|
|
246
302
|
"GET /api/events/ai/tool-bundle -> AiToolBundleController.bundle()"
|
|
247
303
|
],
|
|
248
304
|
"calls_services": [],
|
|
249
|
-
"kafka_produces": [
|
|
250
|
-
|
|
305
|
+
"kafka_produces": [
|
|
306
|
+
"quickstart-events",
|
|
307
|
+
"device-deleted",
|
|
308
|
+
"extension-approval-decided"
|
|
309
|
+
],
|
|
310
|
+
"kafka_consumes": [
|
|
311
|
+
"device-registration",
|
|
312
|
+
"quickstart-events",
|
|
313
|
+
"extension-policy-changed"
|
|
314
|
+
],
|
|
251
315
|
"keycloak_realm": "not_found (no static realm; per-tenant issuer resolved dynamically from DB via DbJwtAuthenticationManagerResolver)",
|
|
252
316
|
"keycloak_client_id": "not_found (tenant.client-id/tenant.client-secret env-driven properties exist)",
|
|
253
317
|
"flyway_next_version": "V38 (highest found: V37__endpoint_ai_inventory.sql; sequential V1-V37, no gaps). NOTE: no Flyway Maven dependency in pom.xml despite this migrations directory existing — flagged inconsistency.",
|
|
@@ -256,7 +320,6 @@
|
|
|
256
320
|
"spring_boot_version": "3.4.12",
|
|
257
321
|
"java_version": "17"
|
|
258
322
|
},
|
|
259
|
-
|
|
260
323
|
"sfn-gateway-api": {
|
|
261
324
|
"artifact_id": "api-gateway (project name: sfn-gateway-api)",
|
|
262
325
|
"port": 8083,
|
|
@@ -274,7 +337,12 @@
|
|
|
274
337
|
"ROUTE /openapi/events -> EVENTS_SERVICE_URI (SetPath /v3/api-docs)",
|
|
275
338
|
"ROUTE /openapi/policy -> POLICY_SERVICE_URI (SetPath /v3/api-docs)"
|
|
276
339
|
],
|
|
277
|
-
"calls_services": [
|
|
340
|
+
"calls_services": [
|
|
341
|
+
"IAM-SERVICE",
|
|
342
|
+
"TENANT-SERVICE",
|
|
343
|
+
"EVENTS-SERVICE",
|
|
344
|
+
"POLICY-SERVICE"
|
|
345
|
+
],
|
|
278
346
|
"kafka_produces": [],
|
|
279
347
|
"kafka_consumes": [],
|
|
280
348
|
"keycloak_realm": "not_found",
|
|
@@ -285,13 +353,54 @@
|
|
|
285
353
|
"spring_boot_version": "3.2.4",
|
|
286
354
|
"java_version": "17"
|
|
287
355
|
},
|
|
288
|
-
|
|
289
356
|
"sfn-iam-api": {
|
|
290
357
|
"artifact_id": "IAM (project name: sfn-iam-api)",
|
|
291
358
|
"port": 8084,
|
|
292
359
|
"eureka_name": "IAM-SERVICE",
|
|
293
360
|
"domain": "Identity & access management: users, groups, roles, scopes, features/packages/subscriptions, SSO configuration, Azure AD group sync, login/device audit. Richest tenant-isolation implementation (TenantAccessInterceptor + ParentChainAuthorizer). Heavy table/endpoint overlap with sfn-auth-api (see manual_review_flags).",
|
|
294
|
-
"owns_tables": [
|
|
361
|
+
"owns_tables": [
|
|
362
|
+
"access_level",
|
|
363
|
+
"addon_pricing",
|
|
364
|
+
"Address",
|
|
365
|
+
"api_flags",
|
|
366
|
+
"audit_log",
|
|
367
|
+
"auth_provider_config",
|
|
368
|
+
"billing_cycle",
|
|
369
|
+
"browserpolicy",
|
|
370
|
+
"cities",
|
|
371
|
+
"country",
|
|
372
|
+
"device_user",
|
|
373
|
+
"events",
|
|
374
|
+
"events_group",
|
|
375
|
+
"events_group_history",
|
|
376
|
+
"extension_policy",
|
|
377
|
+
"features",
|
|
378
|
+
"feature_group",
|
|
379
|
+
"feature_types",
|
|
380
|
+
"groups",
|
|
381
|
+
"industry",
|
|
382
|
+
"login_audit_event",
|
|
383
|
+
"networkpolicy",
|
|
384
|
+
"package",
|
|
385
|
+
"package_feature_mapping",
|
|
386
|
+
"package_pricing",
|
|
387
|
+
"package_type",
|
|
388
|
+
"policy_assignments",
|
|
389
|
+
"region",
|
|
390
|
+
"retention_period",
|
|
391
|
+
"roles",
|
|
392
|
+
"scopes",
|
|
393
|
+
"sso_configurations",
|
|
394
|
+
"sso_provider_url_config",
|
|
395
|
+
"states",
|
|
396
|
+
"Tenant",
|
|
397
|
+
"tenant_addon_feature",
|
|
398
|
+
"tenant_api_mappings",
|
|
399
|
+
"tenant_subscription",
|
|
400
|
+
"tenant_types",
|
|
401
|
+
"Users",
|
|
402
|
+
"user_device_login"
|
|
403
|
+
],
|
|
295
404
|
"exposes_endpoints": [
|
|
296
405
|
"GET /access-levels -> AccessLevelController.getAll()",
|
|
297
406
|
"GET /access-levels/active -> AccessLevelController.getActive()",
|
|
@@ -476,8 +585,12 @@
|
|
|
476
585
|
"POST /users/tenants/{tenantId}/users/{userId}/resend-verification -> UserController.resendVerification()"
|
|
477
586
|
],
|
|
478
587
|
"calls_services": [],
|
|
479
|
-
"kafka_produces": [
|
|
480
|
-
|
|
588
|
+
"kafka_produces": [
|
|
589
|
+
"device-registration"
|
|
590
|
+
],
|
|
591
|
+
"kafka_consumes": [
|
|
592
|
+
"device-deleted"
|
|
593
|
+
],
|
|
481
594
|
"keycloak_realm": "not_found (no static realm; per-tenant DB-driven JwtDecoder resolution via DbJwtAuthenticationManagerResolver; admin-client realm=${KC_ADMIN_REALM})",
|
|
482
595
|
"keycloak_client_id": "not_found (env-driven ${KC_ADMIN_CLIENT_ID}); Keycloak Admin REST client + Microsoft Graph SDK used for Azure AD group sync",
|
|
483
596
|
"flyway_next_version": "V56 (highest found: V55__seed_ai_policy_scopes.sql; non-contiguous range, V41-V47 and V52 missing). NOTE: no Flyway Maven dependency declared despite this migrations directory — flagged inconsistency.",
|
|
@@ -486,13 +599,54 @@
|
|
|
486
599
|
"spring_boot_version": "3.4.12",
|
|
487
600
|
"java_version": "17"
|
|
488
601
|
},
|
|
489
|
-
|
|
490
602
|
"sfn-policy-api": {
|
|
491
603
|
"artifact_id": "sfn-policy-api",
|
|
492
604
|
"port": 8089,
|
|
493
605
|
"eureka_name": "POLICY-SERVICE",
|
|
494
606
|
"domain": "Browser/network/extension/AI policy CRUD and effective-policy resolution for the browser extension; rules-engine bundle publishing. Heavy table/endpoint/Kafka-topic overlap with sfn-tenants-api (see manual_review_flags) — near-duplicate policy domain.",
|
|
495
|
-
"owns_tables": [
|
|
607
|
+
"owns_tables": [
|
|
608
|
+
"Address",
|
|
609
|
+
"api_flags",
|
|
610
|
+
"audit_log",
|
|
611
|
+
"auth_provider_config",
|
|
612
|
+
"billing_cycle",
|
|
613
|
+
"devices",
|
|
614
|
+
"events",
|
|
615
|
+
"browserpolicy",
|
|
616
|
+
"compliancerules",
|
|
617
|
+
"device_user",
|
|
618
|
+
"dlp",
|
|
619
|
+
"events_groups",
|
|
620
|
+
"events_groups_device_user_map",
|
|
621
|
+
"extension_api_keys",
|
|
622
|
+
"extension_detail",
|
|
623
|
+
"extension_policy",
|
|
624
|
+
"groups",
|
|
625
|
+
"homepage",
|
|
626
|
+
"idp_provider_default_hostname",
|
|
627
|
+
"landingpage",
|
|
628
|
+
"managed_extensions",
|
|
629
|
+
"networkconfiguration",
|
|
630
|
+
"networkpolicy",
|
|
631
|
+
"package_pricing",
|
|
632
|
+
"package_type",
|
|
633
|
+
"policy_assignments",
|
|
634
|
+
"roles",
|
|
635
|
+
"rules_engine_bundle",
|
|
636
|
+
"scopes",
|
|
637
|
+
"shortcut",
|
|
638
|
+
"smtp_config",
|
|
639
|
+
"subscription_history",
|
|
640
|
+
"package",
|
|
641
|
+
"Tenant",
|
|
642
|
+
"tenant_api_mappings",
|
|
643
|
+
"tenant_subscription",
|
|
644
|
+
"tenant_types",
|
|
645
|
+
"urlfilter",
|
|
646
|
+
"Users",
|
|
647
|
+
"watermarking",
|
|
648
|
+
"webhook_configs"
|
|
649
|
+
],
|
|
496
650
|
"exposes_endpoints": [
|
|
497
651
|
"POST /browser-policy -> BrowserPolicyController.createPolicy()",
|
|
498
652
|
"GET /browser-policy/{id} -> BrowserPolicyController.getPolicy()",
|
|
@@ -543,8 +697,13 @@
|
|
|
543
697
|
"GET /.well-known/sfbrowser-bootstrap-jwks -> SfBrowserBootstrapJwksController.jwks() [public]"
|
|
544
698
|
],
|
|
545
699
|
"calls_services": [],
|
|
546
|
-
"kafka_produces": [
|
|
547
|
-
|
|
700
|
+
"kafka_produces": [
|
|
701
|
+
"policy-events",
|
|
702
|
+
"extension-policy-changed"
|
|
703
|
+
],
|
|
704
|
+
"kafka_consumes": [
|
|
705
|
+
"extension-approval-decided"
|
|
706
|
+
],
|
|
548
707
|
"keycloak_realm": "not_found (no static realm; per-tenant DB-driven JwtDecoder resolution)",
|
|
549
708
|
"keycloak_client_id": "not_found (env-driven ${KC_ADMIN_CLIENT_ID}); sfbrowser.bootstrap.expected-azp=sfbrowser-bootstrap for the SfBrowser network-config bootstrap flow",
|
|
550
709
|
"flyway_next_version": "not_found (no db/migration directory and no Flyway dependency in this repo — shares the Postgres schema managed/migrated elsewhere, likely sfn-tenants-api)",
|
|
@@ -553,13 +712,57 @@
|
|
|
553
712
|
"spring_boot_version": "3.4.12",
|
|
554
713
|
"java_version": "17"
|
|
555
714
|
},
|
|
556
|
-
|
|
557
715
|
"sfn-tenants-api": {
|
|
558
716
|
"artifact_id": "sfn-tenants-api",
|
|
559
717
|
"port": "CONFLICT: 9001 (local dev default found in local/.env.local.example within the repo) vs 8085 (TENANT_SERVICE_PORT in shared deployment env config supplied by user) — likely a per-developer local override vs. the shared dev-environment value; flagged for manual verification",
|
|
560
718
|
"eureka_name": "TENANT-SERVICE",
|
|
561
719
|
"domain": "Owns the canonical Tenant entity and lifecycle/onboarding (incl. per-tenant Keycloak realm provisioning), sessions/auth, dashboards, webhooks, SMTP config, subscriptions, plus a second copy of the browser/network/extension/AI policy domain (see manual_review_flags for overlap with sfn-policy-api).",
|
|
562
|
-
"owns_tables": [
|
|
720
|
+
"owns_tables": [
|
|
721
|
+
"Tenant",
|
|
722
|
+
"Users",
|
|
723
|
+
"auth_provider_config",
|
|
724
|
+
"ai_policy",
|
|
725
|
+
"audit_log",
|
|
726
|
+
"events",
|
|
727
|
+
"device_user",
|
|
728
|
+
"devices",
|
|
729
|
+
"browserpolicy",
|
|
730
|
+
"events_group",
|
|
731
|
+
"groups",
|
|
732
|
+
"extension_api_keys",
|
|
733
|
+
"login_audit_event",
|
|
734
|
+
"extension_policy",
|
|
735
|
+
"notifications",
|
|
736
|
+
"landingpage",
|
|
737
|
+
"notification_preferences",
|
|
738
|
+
"networkpolicy",
|
|
739
|
+
"policy_assignments",
|
|
740
|
+
"roles",
|
|
741
|
+
"rules_engine_bundle",
|
|
742
|
+
"smtp_config",
|
|
743
|
+
"tenant_api_mappings",
|
|
744
|
+
"tenant_subscription",
|
|
745
|
+
"subscription_history",
|
|
746
|
+
"webhook_configs",
|
|
747
|
+
"webhook_delivery_logs",
|
|
748
|
+
"Address",
|
|
749
|
+
"api_flags",
|
|
750
|
+
"billing_cycle",
|
|
751
|
+
"country",
|
|
752
|
+
"cities",
|
|
753
|
+
"states",
|
|
754
|
+
"region",
|
|
755
|
+
"compliancerules",
|
|
756
|
+
"extension_detail",
|
|
757
|
+
"managed_extensions",
|
|
758
|
+
"package_pricing",
|
|
759
|
+
"package_type",
|
|
760
|
+
"package",
|
|
761
|
+
"scopes",
|
|
762
|
+
"TenantTypes",
|
|
763
|
+
"idp_provider_default_hostname",
|
|
764
|
+
"events_groups_device_user_map"
|
|
765
|
+
],
|
|
563
766
|
"exposes_endpoints": [
|
|
564
767
|
"POST /api/tenants -> TenantController.createTenant()",
|
|
565
768
|
"GET /api/tenants/{id}/provisioning-status -> TenantController.getProvisioningStatus()",
|
|
@@ -664,8 +867,13 @@
|
|
|
664
867
|
"PUT /api/tenants/notifications/preferences -> NotificationController.updatePreferences()"
|
|
665
868
|
],
|
|
666
869
|
"calls_services": [],
|
|
667
|
-
"kafka_produces": [
|
|
668
|
-
|
|
870
|
+
"kafka_produces": [
|
|
871
|
+
"policy-events",
|
|
872
|
+
"extension-policy-changed"
|
|
873
|
+
],
|
|
874
|
+
"kafka_consumes": [
|
|
875
|
+
"extension-approval-decided"
|
|
876
|
+
],
|
|
669
877
|
"keycloak_realm": "not_found (no static realm; owns the realm-provisioning logic itself — creates a new Keycloak realm per tenant onboarding, stored in Tenant.realmName)",
|
|
670
878
|
"keycloak_client_id": "not_found (env-driven ${KC_ADMIN_CLIENT_ID}); sfbrowser.bootstrap.master-realm-issuer for SfBrowser bootstrap service-account flow",
|
|
671
879
|
"flyway_next_version": "V64 (highest found: V63__tenant_principal_gate.sql; full V1-V35 then gap, V41-V63 present, V36-V40 missing). NOTE: no Flyway Maven dependency declared despite this migrations directory — flagged inconsistency (consistent with the same pattern in sfn-iam-api and sfn-events-api).",
|
|
@@ -675,7 +883,6 @@
|
|
|
675
883
|
"java_version": "17"
|
|
676
884
|
}
|
|
677
885
|
},
|
|
678
|
-
|
|
679
886
|
"infrastructure": {
|
|
680
887
|
"source_note": "Ports/hosts/credentials in this section were confirmed via a shared deployment .env supplied directly by the user in-conversation (not discovered by the repo file scan itself). Repo-scan-only findings from earlier phases are preserved alongside as cross-checks where they differ.",
|
|
681
888
|
"eureka": {
|
|
@@ -686,7 +893,10 @@
|
|
|
686
893
|
"prefer_ip_address": true,
|
|
687
894
|
"instance_ip_address": "127.0.0.1"
|
|
688
895
|
},
|
|
689
|
-
"gateway": {
|
|
896
|
+
"gateway": {
|
|
897
|
+
"port": 8083,
|
|
898
|
+
"service_name": "API-GATEWAY-SERVICE"
|
|
899
|
+
},
|
|
690
900
|
"keycloak": {
|
|
691
901
|
"admin_server_url": "https://auth.motivitylabs.net",
|
|
692
902
|
"admin_realm": "master",
|
|
@@ -732,7 +942,11 @@
|
|
|
732
942
|
"password": "Cgg8Q~QubDEAPlIqDn8s2azy126H0~oQ-IH-CbWb",
|
|
733
943
|
"from_address": "donotreply@motivitylabs.net"
|
|
734
944
|
},
|
|
735
|
-
"mailhog": {
|
|
945
|
+
"mailhog": {
|
|
946
|
+
"smtp_port": 0,
|
|
947
|
+
"ui_port": 0,
|
|
948
|
+
"note": "not used by this platform — see infrastructure.smtp instead"
|
|
949
|
+
},
|
|
736
950
|
"docker_network": "not_found (no docker-compose.yml exists anywhere in the workspace; services run individually against externally-hosted shared infra — remote Postgres, remote Kafka broker, hosted Keycloak, Azure Communication Services SMTP)",
|
|
737
951
|
"context_paths": {
|
|
738
952
|
"server_root": "/",
|
|
@@ -748,7 +962,6 @@
|
|
|
748
962
|
"note": "Defined in the shared deployment env config (PRODUCT_SERVICE_NAME/PRODUCT_SERVICE_PORT/PRODUCT_SERVICE_URI) but NO corresponding repository exists anywhere in this 18-repo workspace, and no route to it was found in sfn-gateway-api's application.properties. See manual_review_flags."
|
|
749
963
|
}
|
|
750
964
|
},
|
|
751
|
-
|
|
752
965
|
"frontend": {
|
|
753
966
|
"repo": "sfn-web-ui",
|
|
754
967
|
"dev_port": 5173,
|
|
@@ -759,7 +972,6 @@
|
|
|
759
972
|
"auth_token_attachment": "axios request interceptor: tries dynamicKeycloakService.getToken() (silent refresh) first, falls back to Redux auth.token, sets Authorization: Bearer <token>. Matching response interceptor handles 401 via Keycloak refresh or POST /api/tenants/auth/token/refresh, with a failed-request queue to avoid parallel refresh storms.",
|
|
760
973
|
"tenant_id_in_requests": "Hybrid: normally tenantId is embedded as a URL path segment (derived from Redux auth.user.tenantId) per service's own routing convention. For MSSP 'acting tenant' impersonation, the same axios interceptor adds an X-Acting-Tenant header (for tenant-agnostic-path APIs) AND rewrites /api/tenants/... URL path segments to swap in the acting tenant's UUID (implemented in src/services/tenantImpersonation.js)."
|
|
761
974
|
},
|
|
762
|
-
|
|
763
975
|
"chrome_extension": {
|
|
764
976
|
"repo": "snf-browser-extn",
|
|
765
977
|
"manifest_version": 3,
|
|
@@ -767,7 +979,6 @@
|
|
|
767
979
|
"env_switch_mechanism": "chrome.storage.managed (populated via Windows registry/GPO/MDM per storage.managed_schema pointing at schema.json), read into chrome.storage.local. appEnv value ('DEV'|'UAT'|'TR'|'PROD'|'LOCAL') looked up against a hardcoded ENV_DOMAINS map in background.js (DEV->motivitylabs.net, UAT->agenticworkspace.ai, TR->motivitylabs.ai, PROD->secufusion.com; LOCAL routes to http://localhost:9000), combined with a per-tenant tenantHost managed-storage value to build the API base URL.",
|
|
768
980
|
"registry_key_path": "not_found in this repo's extension JS (background.js comments confirm registry reads for Safe Browsing detection, e.g. HKLM\\SOFTWARE\\Policies\\Google\\Chrome\\SafeBrowsingProtectionLevel, are handled by the separate native Windows agent (Go), not the extension; the actual registry key that seeds chrome.storage.managed is written by the MSI installer's Product.wxs, which is outside this repo's scanned scope)"
|
|
769
981
|
},
|
|
770
|
-
|
|
771
982
|
"auth_flow": {
|
|
772
983
|
"type": "OAuth2/OIDC via Keycloak, multi-tenant (one Keycloak realm provisioned per tenant by sfn-tenants-api at onboarding)",
|
|
773
984
|
"token_type": "JWT (Bearer)",
|
|
@@ -798,7 +1009,6 @@
|
|
|
798
1009
|
"mismatch_validation": true
|
|
799
1010
|
}
|
|
800
1011
|
},
|
|
801
|
-
|
|
802
1012
|
"coding_patterns": {
|
|
803
1013
|
"transactional_placement": "Method level only (never class level). @Transactional(readOnly = true) on read methods, plain @Transactional on write methods. Bulk-operation methods catch Exception per-item internally to accumulate a partial-failure result rather than aborting the whole batch.",
|
|
804
1014
|
"exception_handling": "Custom unchecked exceptions (e.g. ResourceNotFoundException, ResourceConflictException) thrown from services, translated centrally by a @ControllerAdvice GlobalExceptionHandler into a flat JSON error body. Controllers additionally often wrap logic in try/catch to log with tenant/id context before rethrowing, which is somewhat redundant with the global handler.",
|
|
@@ -811,9 +1021,9 @@
|
|
|
811
1021
|
"test_naming_convention": "Not fully uniform. Predominantly camelCase descriptive sentence-style method names (e.g. refreshReplacesStaleSecretOnExistingRowAndPrimesCache); some newer test classes instead use snake_case sentence names with @Nested + @DisplayName groupings (e.g. blacklisted_extension_is_blocked_regardless_of_risk). Class-name suffixes are meaningful and consistent: *Test (unit), *IntegrationTest (Testcontainers-backed), *BaselineTest / *RegressionTest / *GuardrailTest (behavior-pinning regression tests), *ContractTest (Kafka payload contracts), *MigrationTest (Flyway migration verification).",
|
|
812
1022
|
"assertion_library": "AssertJ (assertThat(...)) is the house standard; plain JUnit assertEquals/assertTrue not used in the sampled files.",
|
|
813
1023
|
"query_style": "Mixed within the same repository interfaces: derived query methods (Spring Data naming, incl. nested-property navigation like findByXAndTenant_TenantID), plus @Query using both JPQL and native SQL (native used especially for optional-filter idioms and GROUP BY/aggregate stats queries).",
|
|
814
|
-
"tenant_filter_pattern": "No single universal mechanism. Combination of: (1) explicit tenantId predicate inside @Query/derived repository methods for most tenant-owned entities, (2) a centralized fail-closed HandlerInterceptor (TenantAccessInterceptor, most fully built out in sfn-iam-api and sfn-tenants-api) that validates any tenantId path-variable/query-param against the JWT-resolved acting tenant before the controller runs, (3) explicit service/controller-level TenantAccessGuard.requireCallerCanAccess(...) checks for endpoints addressed by a non-tenant id (e.g. a bare userId), and (4) a small number of repositories/entities are deliberately global/unscoped reference data (countries, package catalogs, scopes, etc.) — see manual_review_flags for entities where this determination could not be fully verified from code alone."
|
|
1024
|
+
"tenant_filter_pattern": "No single universal mechanism. Combination of: (1) explicit tenantId predicate inside @Query/derived repository methods for most tenant-owned entities, (2) a centralized fail-closed HandlerInterceptor (TenantAccessInterceptor, most fully built out in sfn-iam-api and sfn-tenants-api) that validates any tenantId path-variable/query-param against the JWT-resolved acting tenant before the controller runs, (3) explicit service/controller-level TenantAccessGuard.requireCallerCanAccess(...) checks for endpoints addressed by a non-tenant id (e.g. a bare userId), and (4) a small number of repositories/entities are deliberately global/unscoped reference data (countries, package catalogs, scopes, etc.) — see manual_review_flags for entities where this determination could not be fully verified from code alone.",
|
|
1025
|
+
"timestamp_handling": "Architecture Rule: The backend ONLY produces UTC timestamp strings. All time formatting and local timezone conversion MUST be handled by the frontend. The backend never returns pre-formatted display strings like timeDisplay or dateDisplay."
|
|
815
1026
|
},
|
|
816
|
-
|
|
817
1027
|
"golden_rules": {
|
|
818
1028
|
"tenant_isolation": "Multi-layered, not enforced by a single mechanism: JWT issuer resolves caller tenant via per-tenant DB-stored Keycloak config (DbJwtAuthenticationManagerResolver, duplicated across 5 services) -> TenantContextHolder holds the acting tenant for the request -> optional MSSP 'acting tenant' impersonation override via X-Acting-Tenant header or /api/tenants/{tenantId}/... path prefix, gated by ParentChainAuthorizer.isCallerAncestorOrSelf + PrivacyLevelEnforcer.canImpersonate -> TenantAccessInterceptor/TenantAccessGuard perform a fail-closed check of any tenantId path-var/query-param against the acting tenant (most complete in sfn-iam-api and sfn-tenants-api) -> individual repository query methods additionally filter by tenantId for tenant-owned entities. No .rejected-patterns.json file exists anywhere in the workspace to source a canonical, explicitly-authored rule set from — this description is reconstructed from code, not an authored policy document.",
|
|
819
1029
|
"rejected_patterns": [],
|
|
@@ -824,14 +1034,12 @@
|
|
|
824
1034
|
],
|
|
825
1035
|
"explicitly_banned": []
|
|
826
1036
|
},
|
|
827
|
-
|
|
828
1037
|
"current_state": {
|
|
829
1038
|
"active_branch": "Multi-repo — no single active branch. sfn-events-api and sfn-web-ui were checked out on 'develop' at analysis time; snf-browser-extn was checked out on 'main'.",
|
|
830
1039
|
"pending_acs": [],
|
|
831
1040
|
"completed_acs": [],
|
|
832
1041
|
"next_step": "not_found (no .secufusion-state.json exists anywhere in the workspace)"
|
|
833
1042
|
},
|
|
834
|
-
|
|
835
1043
|
"manual_review_flags": [
|
|
836
1044
|
"sfn-auth-api and sfn-iam-api both define an AuthController with near-identical endpoints (GET /tenant-config/v1, GET /tenant-config, POST /login) and both own JPA entities for the same tables (Address, auth_provider_config, groups, roles, scopes, Tenant, Users). It is not determinable from code alone which service is canonical/current and which is legacy or superseded — recommend asking the team directly.",
|
|
837
1045
|
"sfn-tenants-api and sfn-policy-api both implement a near-identical browser/network/extension/AI-policy domain (BrowserPolicyController, ExtensionPolicyController, NetworkPolicyController, LandingPageController, RulesEnginePolicyController, PolicyMappingController, NetworkConfigController, SfBrowserBootstrapJwksController — same class names, same entity/table names, same Kafka topics produced (policy-events, extension-policy-changed) and consumed (extension-approval-decided)). sfn-tenants-api exposes these under /api/tenants/policy/... and additionally owns Tenant lifecycle/onboarding; sfn-policy-api exposes the same functionality under its own /api/policy/... (or similar, context-path is env-driven and not confirmed literally) with no Tenant-ownership responsibilities and only a single smoke test. This strongly suggests one of these two either supersedes the other or was split out and the old copy was never removed — recommend asking the team which is authoritative before adding new policy functionality to either.",
|
|
@@ -847,4 +1055,4 @@
|
|
|
847
1055
|
"The shared deployment env config also defines AUTH_SERVICE_URI=lb://AUTH-SERVICE, but sfn-gateway-api's own application.properties routes list (read directly from the repo) has no /api/auth/** route — only IAM/TENANT/EVENTS/POLICY are routed. Combined with the sfn-auth-api/sfn-iam-api duplicate-AuthController flag above, this is additional evidence that sfn-auth-api may be legacy/unrouted in the current gateway config.",
|
|
848
1056
|
"Two literal port conflicts surfaced when cross-checking the shared deployment env config against values read directly from the repos: sfn-auth-api hardcodes server.port=8081 in application.properties (not wired to any env var) while the shared config defines AUTH_SERVICE_PORT=8087; and sfn-tenants-api's own local/.env.local.example defaults to port 9001 while the shared config defines TENANT_SERVICE_PORT=8085. Both are recorded as CONFLICT values in the affected microservice entries rather than silently picking one — recommend confirming which is actually live in each real environment."
|
|
849
1057
|
]
|
|
850
|
-
}
|
|
1058
|
+
}
|
package/index.js
CHANGED
|
@@ -1191,36 +1191,40 @@ server.tool("classify_task", "MANDATORY FIRST STEP for every task without except
|
|
|
1191
1191
|
const BACKEND_SIGNALS = [
|
|
1192
1192
|
"api", "endpoint", "service", "repository", "database", "db", "query",
|
|
1193
1193
|
"migration", "flyway", "kafka", "consumer", "producer", "topic",
|
|
1194
|
-
"
|
|
1195
|
-
"java", "entity", "@entity", "dto", "controller", "feign", "scheduler",
|
|
1196
|
-
"cron", "config", "yaml", "yml", "null pointer", "exception", "500",
|
|
1197
|
-
"error", "npe", "backend", "server", "rest", "json response", "payload",
|
|
1198
|
-
"header", "cors", "filter", "interceptor", "tenantid", "tenant",
|
|
1199
|
-
"permission", "role", "sfn-iam", "sfn-events", "sfn-tenants",
|
|
1200
|
-
"sfn-policy", "sfn-gateway", "sfn-notification", "sfn-audit", "device",
|
|
1201
|
-
"machine", "event", "security alert", "policy rule", "audit log",
|
|
1194
|
+
"backend", "server", "rest", "json response", "payload"
|
|
1202
1195
|
];
|
|
1203
1196
|
const FRONTEND_SIGNALS = [
|
|
1204
1197
|
"ui", "frontend", "react", "component", "page", "screen", "button",
|
|
1205
|
-
"
|
|
1206
|
-
"dashboard", "layout", "style", "css", "color", "icon", "tooltip",
|
|
1207
|
-
"sidebar", "navbar", "menu", "sfn-web-ui", "web ui", "browser ui",
|
|
1208
|
-
"display", "render", "show on screen", "visual", "animation",
|
|
1209
|
-
"responsive", "redux", "zustand", "context", "hook", "tsx", "jsx",
|
|
1210
|
-
"html",
|
|
1198
|
+
"modal", "layout", "style", "css", "web ui", "browser ui", "render"
|
|
1211
1199
|
];
|
|
1212
1200
|
const EXTENSION_SIGNALS = [
|
|
1213
|
-
"chrome extension", "extension", "
|
|
1214
|
-
"
|
|
1215
|
-
"chrome storage", "registry", "managed policy", "browser extension",
|
|
1216
|
-
"sfn-chrome",
|
|
1201
|
+
"chrome extension", "extension", "content script", "background script",
|
|
1202
|
+
"browser extension", "popup"
|
|
1217
1203
|
];
|
|
1204
|
+
if (spec) {
|
|
1205
|
+
if (spec.microservices) {
|
|
1206
|
+
Object.keys(spec.microservices).forEach(k => BACKEND_SIGNALS.push(k.toLowerCase()));
|
|
1207
|
+
Object.values(spec.microservices).forEach(svc => {
|
|
1208
|
+
if (Array.isArray(svc.owns_tables)) {
|
|
1209
|
+
svc.owns_tables.forEach(t => BACKEND_SIGNALS.push(t.toLowerCase()));
|
|
1210
|
+
}
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
if (spec.frontend && spec.frontend.repo)
|
|
1214
|
+
FRONTEND_SIGNALS.push(spec.frontend.repo.toLowerCase());
|
|
1215
|
+
if (spec.chrome_extension && spec.chrome_extension.repo)
|
|
1216
|
+
EXTENSION_SIGNALS.push(spec.chrome_extension.repo.toLowerCase());
|
|
1217
|
+
}
|
|
1218
1218
|
const backendFound = BACKEND_SIGNALS.filter(s => corpus.includes(s));
|
|
1219
1219
|
const frontendFound = FRONTEND_SIGNALS.filter(s => corpus.includes(s));
|
|
1220
1220
|
const extensionFound = EXTENSION_SIGNALS.filter(s => corpus.includes(s));
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1221
|
+
// Refine signals based on explicit modifiers (the user's "analysis" expectation)
|
|
1222
|
+
let backendScore = backendFound.length;
|
|
1223
|
+
let frontendScore = frontendFound.length;
|
|
1224
|
+
let extensionScore = extensionFound.length;
|
|
1225
|
+
if (corpus.includes("pure ui") || corpus.includes("no backend changes") || corpus.includes("frontend only")) {
|
|
1226
|
+
backendScore = 0;
|
|
1227
|
+
}
|
|
1224
1228
|
// Determine classification
|
|
1225
1229
|
let classification;
|
|
1226
1230
|
let confidence = "HIGH";
|
|
@@ -1258,7 +1262,18 @@ server.tool("classify_task", "MANDATORY FIRST STEP for every task without except
|
|
|
1258
1262
|
// CHECK 1: Title accuracy
|
|
1259
1263
|
const verbs = ["delete", "create", "fetch", "update", "add", "remove", "fix", "implement"];
|
|
1260
1264
|
const nouns = ["tenant", "admin", "device", "policy", "gateway", "notification", "audit", "role", "permission", "user", "group", "mapping", "api", "endpoint", "dashboard", "db", "database"];
|
|
1261
|
-
const services = [
|
|
1265
|
+
const services = [];
|
|
1266
|
+
if (spec) {
|
|
1267
|
+
if (spec.microservices)
|
|
1268
|
+
Object.keys(spec.microservices).forEach(k => services.push(k.toLowerCase()));
|
|
1269
|
+
if (spec.frontend && spec.frontend.repo)
|
|
1270
|
+
services.push(spec.frontend.repo.toLowerCase());
|
|
1271
|
+
if (spec.chrome_extension && spec.chrome_extension.repo)
|
|
1272
|
+
services.push(spec.chrome_extension.repo.toLowerCase());
|
|
1273
|
+
}
|
|
1274
|
+
if (services.length === 0) {
|
|
1275
|
+
services.push("sfn-iam-api", "sfn-events-api", "sfn-tenants-api", "sfn-policy-api", "sfn-gateway-api", "sfn-web-ui");
|
|
1276
|
+
}
|
|
1262
1277
|
const foundConcepts = [];
|
|
1263
1278
|
[...verbs, ...nouns, ...services].forEach(word => {
|
|
1264
1279
|
if (descLower.includes(word))
|
|
@@ -1376,8 +1391,15 @@ server.tool("classify_task", "MANDATORY FIRST STEP for every task without except
|
|
|
1376
1391
|
secufusionFlags.push(`This may re-introduce rejected pattern #${r.id}: ${r.pattern}`);
|
|
1377
1392
|
}
|
|
1378
1393
|
});
|
|
1379
|
-
|
|
1380
|
-
|
|
1394
|
+
// Dynamically inject coding patterns from project spec
|
|
1395
|
+
if (spec && spec.coding_patterns) {
|
|
1396
|
+
Object.entries(spec.coding_patterns).forEach(([key, rule]) => {
|
|
1397
|
+
const keyTokens = key.toLowerCase().split('_');
|
|
1398
|
+
// if any token from the key is in the corpus, add the rule
|
|
1399
|
+
if (keyTokens.some(token => corpus.includes(token))) {
|
|
1400
|
+
secufusionFlags.push(`Architecture Rule (${key}): ${rule}`);
|
|
1401
|
+
}
|
|
1402
|
+
});
|
|
1381
1403
|
}
|
|
1382
1404
|
let validationVerdict = "CLEAN";
|
|
1383
1405
|
if (!titleAccurate || !taskTypeCorrect || (task_type === "bug" && rootCauseAssumed)) {
|
|
@@ -1416,6 +1438,16 @@ server.tool("classify_task", "MANDATORY FIRST STEP for every task without except
|
|
|
1416
1438
|
"column", "table", "migration", "flyway", "entity", "@entity",
|
|
1417
1439
|
"schema", "alter", "add column", "drop column", "rename column",
|
|
1418
1440
|
];
|
|
1441
|
+
if (spec && spec.microservices) {
|
|
1442
|
+
Object.values(spec.microservices).forEach(svc => {
|
|
1443
|
+
if (Array.isArray(svc.owns_tables)) {
|
|
1444
|
+
svc.owns_tables.forEach(t => DB_CHANGE_SIGNALS.push(t.toLowerCase()));
|
|
1445
|
+
}
|
|
1446
|
+
if (Array.isArray(svc.kafka_consumes)) {
|
|
1447
|
+
svc.kafka_consumes.forEach(topic => KAFKA_CHANGE_SIGNALS.push(topic.toLowerCase()));
|
|
1448
|
+
}
|
|
1449
|
+
});
|
|
1450
|
+
}
|
|
1419
1451
|
const endpointRisk = ENDPOINT_CHANGE_SIGNALS.some(s => corpus.includes(s));
|
|
1420
1452
|
const kafkaRisk = KAFKA_CHANGE_SIGNALS.some(s => corpus.includes(s));
|
|
1421
1453
|
const dbRisk = DB_CHANGE_SIGNALS.some(s => corpus.includes(s));
|