@akasecurity/cli 0.9.0-rc1 → 0.9.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.
- package/README.md +3 -3
- package/dist/cli.js +2559 -546
- package/package.json +4 -4
- package/web-ui/web-ui/.next/BUILD_ID +1 -1
- package/web-ui/web-ui/.next/app-build-manifest.json +58 -58
- package/web-ui/web-ui/.next/app-path-routes-manifest.json +7 -7
- package/web-ui/web-ui/.next/build-manifest.json +2 -2
- package/web-ui/web-ui/.next/prerender-manifest.json +3 -3
- package/web-ui/web-ui/.next/server/app/(app)/activity/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/activity/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/activity/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/data-shares/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/data-shares/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/data-shares/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/detections/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/detections/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/detections/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/[id]/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/[id]/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/[id]/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/new/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/new/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/new/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/page.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/exceptions/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/findings/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/findings/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/findings/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/inventory/page.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/inventory/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/inventory/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/policies/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/policies/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/policies/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/scan/page.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/scan/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/scan/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/security/page.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/security/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/security/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/settings/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/settings/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/settings/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/updates/page.js +2 -2
- package/web-ui/web-ui/.next/server/app/(app)/updates/page.js.nft.json +1 -1
- package/web-ui/web-ui/.next/server/app/(app)/updates/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app/_not-found.html +1 -1
- package/web-ui/web-ui/.next/server/app/_not-found.rsc +2 -2
- package/web-ui/web-ui/.next/server/app/index.html +1 -1
- package/web-ui/web-ui/.next/server/app/index.rsc +2 -2
- package/web-ui/web-ui/.next/server/app/page_client-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/app-paths-manifest.json +7 -7
- package/web-ui/web-ui/.next/server/chunks/{366.js → 244.js} +92 -25
- package/web-ui/web-ui/.next/server/chunks/264.js +1 -0
- package/web-ui/web-ui/.next/server/chunks/291.js +647 -0
- package/web-ui/web-ui/.next/server/chunks/45.js +1 -1
- package/web-ui/web-ui/.next/server/chunks/461.js +5 -5
- package/web-ui/web-ui/.next/server/chunks/773.js +4 -4
- package/web-ui/web-ui/.next/server/chunks/983.js +2 -0
- package/web-ui/web-ui/.next/server/functions-config-manifest.json +5 -5
- package/web-ui/web-ui/.next/server/middleware-manifest.json +5 -5
- package/web-ui/web-ui/.next/server/pages/404.html +1 -1
- package/web-ui/web-ui/.next/server/pages/500.html +1 -1
- package/web-ui/web-ui/.next/server/server-reference-manifest.js +1 -1
- package/web-ui/web-ui/.next/server/server-reference-manifest.json +1 -1
- package/web-ui/web-ui/.next/static/chunks/{24-55b96836aea5ce23.js → 24-d36fb6bd72e29537.js} +2 -2
- package/web-ui/web-ui/.next/static/chunks/561-52ad6ae6bf64b471.js +1 -0
- package/web-ui/web-ui/.next/static/chunks/app/(app)/activity/{page-0c208c8b275a69ac.js → page-797e2ce43b8db7e1.js} +1 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/data-shares/{page-041187c76bd42461.js → page-d9e9023871035a2d.js} +1 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/detections/page-5bd8e04955058e46.js +1 -0
- package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/[id]/page-a254c9fc7cc27b97.js +1 -0
- package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/new/{page-2cfe42c3dd4833c3.js → page-622151ff8e6960c8.js} +1 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/page-c1221106bd6ba356.js +1 -0
- package/web-ui/web-ui/.next/static/chunks/app/(app)/findings/{page-d33f3999e89c7e77.js → page-fc68b0fb9b669a03.js} +1 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/inventory/page-68418378cfb10a0f.js +1 -0
- package/web-ui/web-ui/.next/static/chunks/app/(app)/scan/page-2b9ea3e95dbe82fb.js +1 -0
- package/web-ui/web-ui/.next/static/chunks/app/(app)/settings/page-315b8b57329d5eb6.js +1 -0
- package/web-ui/web-ui/.next/static/chunks/app/(app)/updates/page-77adb595bb447040.js +1 -0
- package/web-ui/web-ui/.next/static/chunks/app/{layout-40fea3e270a8e120.js → layout-8b19b03146218dc7.js} +1 -1
- package/web-ui/web-ui/.next/static/css/{43f95c94ecc1bd17.css → 500a1c2bf0fc64fb.css} +1 -1
- package/web-ui/web-ui/.next/server/chunks/484.js +0 -580
- package/web-ui/web-ui/.next/server/chunks/900.js +0 -1
- package/web-ui/web-ui/.next/server/chunks/918.js +0 -2
- package/web-ui/web-ui/.next/static/chunks/561-c3388f066f813e17.js +0 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/detections/page-481fedcbfb474cef.js +0 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/[id]/page-a20e7d7a2f0ad507.js +0 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/exceptions/page-849d66cc2cd77cb6.js +0 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/inventory/page-76edd10592ba3cb1.js +0 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/scan/page-50b604f229747240.js +0 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/settings/page-92d1086c8e8ed3bf.js +0 -1
- package/web-ui/web-ui/.next/static/chunks/app/(app)/updates/page-c3c1671d32547619.js +0 -1
- /package/web-ui/web-ui/.next/static/{unUYwnPVSRwxtrUFgSAxr → ez8SLWYcUCScGuFekZ-G_}/_buildManifest.js +0 -0
- /package/web-ui/web-ui/.next/static/{unUYwnPVSRwxtrUFgSAxr → ez8SLWYcUCScGuFekZ-G_}/_ssgManifest.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -750,18 +750,1420 @@ function installAgentPlugin(agentId, mode = "capture") {
|
|
|
750
750
|
return run("claude", ["plugin", "install", resolved.ref], mode);
|
|
751
751
|
}
|
|
752
752
|
|
|
753
|
-
// ../packages/local-ops/src/
|
|
754
|
-
import {
|
|
755
|
-
import {
|
|
756
|
-
|
|
753
|
+
// ../packages/local-ops/src/egress-record.ts
|
|
754
|
+
import { statSync as statSync4 } from "fs";
|
|
755
|
+
import { dirname as dirname4, relative as relative2, resolve } from "path";
|
|
756
|
+
|
|
757
|
+
// ../packages/detections/src/egress/registry.ts
|
|
758
|
+
var EXTRACTOR_VERSION = "1";
|
|
759
|
+
var PROVIDER_REGISTRY = [
|
|
760
|
+
{
|
|
761
|
+
id: "stripe",
|
|
762
|
+
name: "Stripe",
|
|
763
|
+
category: "Payments",
|
|
764
|
+
hostSuffixes: ["stripe.com"],
|
|
765
|
+
apiBase: "https://api.stripe.com",
|
|
766
|
+
defaultDataClasses: ["pii", "customer"],
|
|
767
|
+
sdks: {
|
|
768
|
+
npm: ["stripe"],
|
|
769
|
+
pypi: ["stripe"],
|
|
770
|
+
go: ["github.com/stripe/stripe-go"],
|
|
771
|
+
maven: ["com.stripe"],
|
|
772
|
+
rubygems: ["stripe"],
|
|
773
|
+
composer: ["stripe/stripe-php"],
|
|
774
|
+
nuget: ["Stripe.net"]
|
|
775
|
+
}
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
id: "datadog",
|
|
779
|
+
name: "Datadog",
|
|
780
|
+
category: "Observability",
|
|
781
|
+
hostSuffixes: ["datadoghq.com", "datadoghq.eu"],
|
|
782
|
+
apiBase: "https://api.datadoghq.com",
|
|
783
|
+
defaultDataClasses: ["telemetry", "logs", "metrics"],
|
|
784
|
+
sdks: {
|
|
785
|
+
npm: ["dd-trace", "@datadog/browser-logs"],
|
|
786
|
+
pypi: ["datadog", "ddtrace"],
|
|
787
|
+
go: ["github.com/DataDog/dd-trace-go"],
|
|
788
|
+
maven: ["com.datadoghq"],
|
|
789
|
+
rubygems: ["ddtrace", "dogapi"],
|
|
790
|
+
nuget: ["Datadog.Trace"]
|
|
791
|
+
}
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
id: "newrelic",
|
|
795
|
+
name: "New Relic",
|
|
796
|
+
category: "Observability",
|
|
797
|
+
hostSuffixes: ["newrelic.com", "nr-data.net"],
|
|
798
|
+
apiBase: "https://api.newrelic.com",
|
|
799
|
+
defaultDataClasses: ["telemetry", "logs", "metrics"],
|
|
800
|
+
sdks: {
|
|
801
|
+
npm: ["newrelic"],
|
|
802
|
+
pypi: ["newrelic"],
|
|
803
|
+
go: ["github.com/newrelic/go-agent"],
|
|
804
|
+
maven: ["com.newrelic.agent.java"],
|
|
805
|
+
rubygems: ["newrelic_rpm"],
|
|
806
|
+
nuget: ["NewRelic.Agent"]
|
|
807
|
+
}
|
|
808
|
+
},
|
|
809
|
+
{
|
|
810
|
+
id: "sentry",
|
|
811
|
+
name: "Sentry",
|
|
812
|
+
category: "Error tracking",
|
|
813
|
+
hostSuffixes: ["sentry.io"],
|
|
814
|
+
apiBase: "https://sentry.io",
|
|
815
|
+
defaultDataClasses: ["source", "telemetry"],
|
|
816
|
+
sdks: {
|
|
817
|
+
npm: ["@sentry/node", "@sentry/react", "@sentry/nextjs"],
|
|
818
|
+
pypi: ["sentry-sdk"],
|
|
819
|
+
go: ["github.com/getsentry/sentry-go"],
|
|
820
|
+
maven: ["io.sentry"],
|
|
821
|
+
rubygems: ["sentry-ruby"],
|
|
822
|
+
cargo: ["sentry"],
|
|
823
|
+
composer: ["sentry/sentry"],
|
|
824
|
+
nuget: ["Sentry"]
|
|
825
|
+
}
|
|
826
|
+
},
|
|
827
|
+
{
|
|
828
|
+
id: "openai",
|
|
829
|
+
name: "OpenAI",
|
|
830
|
+
category: "LLM provider",
|
|
831
|
+
hostSuffixes: ["openai.com"],
|
|
832
|
+
apiBase: "https://api.openai.com",
|
|
833
|
+
defaultDataClasses: ["pii", "source"],
|
|
834
|
+
sdks: {
|
|
835
|
+
npm: ["openai"],
|
|
836
|
+
pypi: ["openai"],
|
|
837
|
+
go: ["github.com/sashabaranov/go-openai"],
|
|
838
|
+
maven: ["com.openai"],
|
|
839
|
+
rubygems: ["ruby-openai"],
|
|
840
|
+
cargo: ["async-openai"],
|
|
841
|
+
composer: ["openai-php/client"],
|
|
842
|
+
nuget: ["OpenAI"]
|
|
843
|
+
}
|
|
844
|
+
},
|
|
845
|
+
{
|
|
846
|
+
id: "anthropic",
|
|
847
|
+
name: "Anthropic",
|
|
848
|
+
category: "LLM provider",
|
|
849
|
+
hostSuffixes: ["anthropic.com"],
|
|
850
|
+
apiBase: "https://api.anthropic.com",
|
|
851
|
+
defaultDataClasses: ["pii", "source"],
|
|
852
|
+
sdks: {
|
|
853
|
+
npm: ["@anthropic-ai/sdk"],
|
|
854
|
+
pypi: ["anthropic"],
|
|
855
|
+
go: ["github.com/anthropics/anthropic-sdk-go"],
|
|
856
|
+
nuget: ["Anthropic.SDK"]
|
|
857
|
+
}
|
|
858
|
+
},
|
|
859
|
+
{
|
|
860
|
+
id: "aws",
|
|
861
|
+
name: "Amazon Web Services",
|
|
862
|
+
category: "Cloud platform",
|
|
863
|
+
hostSuffixes: ["amazonaws.com"],
|
|
864
|
+
apiBase: "https://s3.amazonaws.com",
|
|
865
|
+
defaultDataClasses: ["secrets", "customer"],
|
|
866
|
+
sdks: {
|
|
867
|
+
npm: ["@aws-sdk/client-s3", "aws-sdk"],
|
|
868
|
+
pypi: ["boto3"],
|
|
869
|
+
go: ["github.com/aws/aws-sdk-go", "github.com/aws/aws-sdk-go-v2"],
|
|
870
|
+
maven: ["com.amazonaws", "software.amazon.awssdk"],
|
|
871
|
+
rubygems: ["aws-sdk-s3"],
|
|
872
|
+
cargo: ["aws-sdk-s3"],
|
|
873
|
+
nuget: ["AWSSDK.S3"]
|
|
874
|
+
}
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
id: "gcp",
|
|
878
|
+
name: "Google Cloud",
|
|
879
|
+
category: "Cloud platform",
|
|
880
|
+
hostSuffixes: ["googleapis.com"],
|
|
881
|
+
apiBase: "https://storage.googleapis.com",
|
|
882
|
+
defaultDataClasses: ["customer", "logs"],
|
|
883
|
+
sdks: {
|
|
884
|
+
npm: ["@google-cloud/storage"],
|
|
885
|
+
pypi: ["google-cloud-storage"],
|
|
886
|
+
go: ["cloud.google.com/go"],
|
|
887
|
+
maven: ["com.google.cloud"],
|
|
888
|
+
rubygems: ["google-cloud-storage"],
|
|
889
|
+
nuget: ["Google.Cloud.Storage.V1"]
|
|
890
|
+
}
|
|
891
|
+
},
|
|
892
|
+
{
|
|
893
|
+
id: "azure",
|
|
894
|
+
name: "Microsoft Azure",
|
|
895
|
+
category: "Cloud platform",
|
|
896
|
+
hostSuffixes: ["azure.com", "windows.net"],
|
|
897
|
+
apiBase: "https://management.azure.com",
|
|
898
|
+
defaultDataClasses: ["customer", "logs"],
|
|
899
|
+
sdks: {
|
|
900
|
+
npm: ["@azure/storage-blob"],
|
|
901
|
+
pypi: ["azure-storage-blob"],
|
|
902
|
+
go: ["github.com/Azure/azure-sdk-for-go"],
|
|
903
|
+
maven: ["com.azure"],
|
|
904
|
+
rubygems: ["azure-storage-blob"],
|
|
905
|
+
nuget: ["Azure.Storage.Blobs"]
|
|
906
|
+
}
|
|
907
|
+
},
|
|
908
|
+
{
|
|
909
|
+
id: "slack",
|
|
910
|
+
name: "Slack",
|
|
911
|
+
category: "Notifications",
|
|
912
|
+
hostSuffixes: ["slack.com"],
|
|
913
|
+
apiBase: "https://slack.com/api",
|
|
914
|
+
defaultDataClasses: ["logs"],
|
|
915
|
+
sdks: {
|
|
916
|
+
npm: ["@slack/web-api"],
|
|
917
|
+
pypi: ["slack-sdk"],
|
|
918
|
+
go: ["github.com/slack-go/slack"],
|
|
919
|
+
maven: ["com.slack.api"],
|
|
920
|
+
rubygems: ["slack-ruby-client"],
|
|
921
|
+
composer: ["slack-php/slack-api"],
|
|
922
|
+
nuget: ["SlackNet"]
|
|
923
|
+
}
|
|
924
|
+
},
|
|
925
|
+
{
|
|
926
|
+
id: "segment",
|
|
927
|
+
name: "Segment",
|
|
928
|
+
category: "Analytics",
|
|
929
|
+
hostSuffixes: ["segment.io", "segment.com"],
|
|
930
|
+
apiBase: "https://api.segment.io",
|
|
931
|
+
defaultDataClasses: ["customer"],
|
|
932
|
+
sdks: {
|
|
933
|
+
npm: ["@segment/analytics-node", "analytics-node"],
|
|
934
|
+
pypi: ["segment-analytics-python"],
|
|
935
|
+
go: ["github.com/segmentio/analytics-go"],
|
|
936
|
+
maven: ["com.segment.analytics.java"],
|
|
937
|
+
rubygems: ["analytics-ruby"],
|
|
938
|
+
nuget: ["Analytics"]
|
|
939
|
+
}
|
|
940
|
+
},
|
|
941
|
+
{
|
|
942
|
+
id: "twilio",
|
|
943
|
+
name: "Twilio",
|
|
944
|
+
category: "Communications",
|
|
945
|
+
hostSuffixes: ["twilio.com"],
|
|
946
|
+
apiBase: "https://api.twilio.com",
|
|
947
|
+
defaultDataClasses: ["pii", "customer"],
|
|
948
|
+
sdks: {
|
|
949
|
+
npm: ["twilio"],
|
|
950
|
+
pypi: ["twilio"],
|
|
951
|
+
go: ["github.com/twilio/twilio-go"],
|
|
952
|
+
maven: ["com.twilio.sdk"],
|
|
953
|
+
rubygems: ["twilio-ruby"],
|
|
954
|
+
composer: ["twilio/sdk"],
|
|
955
|
+
nuget: ["Twilio"]
|
|
956
|
+
}
|
|
957
|
+
},
|
|
958
|
+
{
|
|
959
|
+
id: "sendgrid",
|
|
960
|
+
name: "SendGrid",
|
|
961
|
+
category: "Email",
|
|
962
|
+
hostSuffixes: ["sendgrid.com"],
|
|
963
|
+
apiBase: "https://api.sendgrid.com",
|
|
964
|
+
defaultDataClasses: ["pii"],
|
|
965
|
+
sdks: {
|
|
966
|
+
npm: ["@sendgrid/mail"],
|
|
967
|
+
pypi: ["sendgrid"],
|
|
968
|
+
go: ["github.com/sendgrid/sendgrid-go"],
|
|
969
|
+
maven: ["com.sendgrid"],
|
|
970
|
+
rubygems: ["sendgrid-ruby"],
|
|
971
|
+
composer: ["sendgrid/sendgrid"],
|
|
972
|
+
nuget: ["SendGrid"]
|
|
973
|
+
}
|
|
974
|
+
},
|
|
975
|
+
{
|
|
976
|
+
id: "mailgun",
|
|
977
|
+
name: "Mailgun",
|
|
978
|
+
category: "Email",
|
|
979
|
+
hostSuffixes: ["mailgun.net"],
|
|
980
|
+
apiBase: "https://api.mailgun.net",
|
|
981
|
+
defaultDataClasses: ["pii"],
|
|
982
|
+
sdks: {
|
|
983
|
+
npm: ["mailgun.js"],
|
|
984
|
+
pypi: ["mailgun"],
|
|
985
|
+
rubygems: ["mailgun-ruby"],
|
|
986
|
+
composer: ["mailgun/mailgun-php"],
|
|
987
|
+
nuget: ["Mailgun"]
|
|
988
|
+
}
|
|
989
|
+
},
|
|
990
|
+
{
|
|
991
|
+
id: "mixpanel",
|
|
992
|
+
name: "Mixpanel",
|
|
993
|
+
category: "Analytics",
|
|
994
|
+
hostSuffixes: ["mixpanel.com"],
|
|
995
|
+
apiBase: "https://api.mixpanel.com",
|
|
996
|
+
defaultDataClasses: ["customer", "telemetry"],
|
|
997
|
+
sdks: {
|
|
998
|
+
npm: ["mixpanel"],
|
|
999
|
+
pypi: ["mixpanel"],
|
|
1000
|
+
rubygems: ["mixpanel-ruby"],
|
|
1001
|
+
nuget: ["Mixpanel"]
|
|
1002
|
+
}
|
|
1003
|
+
},
|
|
1004
|
+
{
|
|
1005
|
+
id: "amplitude",
|
|
1006
|
+
name: "Amplitude",
|
|
1007
|
+
category: "Analytics",
|
|
1008
|
+
hostSuffixes: ["amplitude.com"],
|
|
1009
|
+
apiBase: "https://api2.amplitude.com",
|
|
1010
|
+
defaultDataClasses: ["customer", "telemetry"],
|
|
1011
|
+
sdks: {
|
|
1012
|
+
npm: ["@amplitude/analytics-node"],
|
|
1013
|
+
pypi: ["amplitude-analytics"],
|
|
1014
|
+
nuget: ["Amplitude"]
|
|
1015
|
+
}
|
|
1016
|
+
},
|
|
1017
|
+
{
|
|
1018
|
+
id: "posthog",
|
|
1019
|
+
name: "PostHog",
|
|
1020
|
+
category: "Analytics",
|
|
1021
|
+
hostSuffixes: ["posthog.com"],
|
|
1022
|
+
apiBase: "https://us.i.posthog.com",
|
|
1023
|
+
defaultDataClasses: ["customer", "telemetry"],
|
|
1024
|
+
sdks: {
|
|
1025
|
+
npm: ["posthog-node", "posthog-js"],
|
|
1026
|
+
pypi: ["posthog"],
|
|
1027
|
+
go: ["github.com/posthog/posthog-go"],
|
|
1028
|
+
rubygems: ["posthog-ruby"],
|
|
1029
|
+
composer: ["posthog/posthog-php"],
|
|
1030
|
+
nuget: ["PostHog"]
|
|
1031
|
+
}
|
|
1032
|
+
},
|
|
1033
|
+
{
|
|
1034
|
+
id: "honeycomb",
|
|
1035
|
+
name: "Honeycomb",
|
|
1036
|
+
category: "Observability",
|
|
1037
|
+
hostSuffixes: ["honeycomb.io"],
|
|
1038
|
+
apiBase: "https://api.honeycomb.io",
|
|
1039
|
+
defaultDataClasses: ["telemetry", "metrics"],
|
|
1040
|
+
sdks: {
|
|
1041
|
+
npm: ["libhoney"],
|
|
1042
|
+
pypi: ["libhoney"],
|
|
1043
|
+
go: ["github.com/honeycombio/libhoney-go"],
|
|
1044
|
+
rubygems: ["libhoney"]
|
|
1045
|
+
}
|
|
1046
|
+
},
|
|
1047
|
+
{
|
|
1048
|
+
id: "grafana",
|
|
1049
|
+
name: "Grafana Cloud",
|
|
1050
|
+
category: "Observability",
|
|
1051
|
+
hostSuffixes: ["grafana.net"],
|
|
1052
|
+
apiBase: "https://grafana.net",
|
|
1053
|
+
defaultDataClasses: ["logs", "metrics"],
|
|
1054
|
+
sdks: {
|
|
1055
|
+
npm: ["@grafana/faro-web-sdk"]
|
|
1056
|
+
}
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
id: "splunk",
|
|
1060
|
+
name: "Splunk",
|
|
1061
|
+
category: "Observability",
|
|
1062
|
+
hostSuffixes: ["splunkcloud.com", "splunk.com"],
|
|
1063
|
+
apiBase: "https://http-inputs.splunkcloud.com",
|
|
1064
|
+
defaultDataClasses: ["logs"],
|
|
1065
|
+
sdks: {
|
|
1066
|
+
npm: ["splunk-logging"],
|
|
1067
|
+
pypi: ["splunk-sdk"],
|
|
1068
|
+
maven: ["com.splunk"],
|
|
1069
|
+
nuget: ["Splunk.Logging.Common"]
|
|
1070
|
+
}
|
|
1071
|
+
},
|
|
1072
|
+
{
|
|
1073
|
+
id: "pagerduty",
|
|
1074
|
+
name: "PagerDuty",
|
|
1075
|
+
category: "Incident response",
|
|
1076
|
+
hostSuffixes: ["pagerduty.com"],
|
|
1077
|
+
apiBase: "https://api.pagerduty.com",
|
|
1078
|
+
defaultDataClasses: ["logs"],
|
|
1079
|
+
sdks: {
|
|
1080
|
+
npm: ["@pagerduty/pdjs"],
|
|
1081
|
+
pypi: ["pdpyras"],
|
|
1082
|
+
go: ["github.com/PagerDuty/go-pagerduty"],
|
|
1083
|
+
rubygems: ["pagerduty"]
|
|
1084
|
+
}
|
|
1085
|
+
},
|
|
1086
|
+
{
|
|
1087
|
+
id: "github",
|
|
1088
|
+
name: "GitHub",
|
|
1089
|
+
category: "Developer platform",
|
|
1090
|
+
hostSuffixes: ["github.com", "githubusercontent.com"],
|
|
1091
|
+
apiBase: "https://api.github.com",
|
|
1092
|
+
defaultDataClasses: ["source"],
|
|
1093
|
+
sdks: {
|
|
1094
|
+
npm: ["@octokit/rest", "octokit"],
|
|
1095
|
+
pypi: ["pygithub"],
|
|
1096
|
+
go: ["github.com/google/go-github"],
|
|
1097
|
+
maven: ["org.kohsuke.github-api"],
|
|
1098
|
+
rubygems: ["octokit"],
|
|
1099
|
+
cargo: ["octocrab"],
|
|
1100
|
+
composer: ["knplabs/github-api"],
|
|
1101
|
+
nuget: ["Octokit"]
|
|
1102
|
+
}
|
|
1103
|
+
},
|
|
1104
|
+
{
|
|
1105
|
+
id: "gitlab",
|
|
1106
|
+
name: "GitLab",
|
|
1107
|
+
category: "Developer platform",
|
|
1108
|
+
hostSuffixes: ["gitlab.com"],
|
|
1109
|
+
apiBase: "https://gitlab.com/api",
|
|
1110
|
+
defaultDataClasses: ["source"],
|
|
1111
|
+
sdks: {
|
|
1112
|
+
npm: ["@gitbeaker/rest"],
|
|
1113
|
+
pypi: ["python-gitlab"],
|
|
1114
|
+
go: ["gitlab.com/gitlab-org/api/client-go"],
|
|
1115
|
+
rubygems: ["gitlab"],
|
|
1116
|
+
nuget: ["GitLabApiClient"]
|
|
1117
|
+
}
|
|
1118
|
+
},
|
|
1119
|
+
{
|
|
1120
|
+
id: "auth0",
|
|
1121
|
+
name: "Auth0",
|
|
1122
|
+
category: "Identity",
|
|
1123
|
+
hostSuffixes: ["auth0.com"],
|
|
1124
|
+
apiBase: "https://login.auth0.com",
|
|
1125
|
+
defaultDataClasses: ["pii"],
|
|
1126
|
+
sdks: {
|
|
1127
|
+
npm: ["auth0"],
|
|
1128
|
+
pypi: ["auth0-python"],
|
|
1129
|
+
go: ["github.com/auth0/go-auth0"],
|
|
1130
|
+
maven: ["com.auth0"],
|
|
1131
|
+
rubygems: ["auth0"],
|
|
1132
|
+
composer: ["auth0/auth0-php"],
|
|
1133
|
+
nuget: ["Auth0.ManagementApi"]
|
|
1134
|
+
}
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
id: "okta",
|
|
1138
|
+
name: "Okta",
|
|
1139
|
+
category: "Identity",
|
|
1140
|
+
hostSuffixes: ["okta.com", "oktapreview.com"],
|
|
1141
|
+
apiBase: "https://login.okta.com",
|
|
1142
|
+
defaultDataClasses: ["pii"],
|
|
1143
|
+
sdks: {
|
|
1144
|
+
npm: ["@okta/okta-sdk-nodejs"],
|
|
1145
|
+
pypi: ["okta"],
|
|
1146
|
+
go: ["github.com/okta/okta-sdk-golang"],
|
|
1147
|
+
maven: ["com.okta.sdk"],
|
|
1148
|
+
nuget: ["Okta.Sdk"]
|
|
1149
|
+
}
|
|
1150
|
+
},
|
|
1151
|
+
{
|
|
1152
|
+
id: "clerk",
|
|
1153
|
+
name: "Clerk",
|
|
1154
|
+
category: "Identity",
|
|
1155
|
+
hostSuffixes: ["clerk.com", "clerk.dev"],
|
|
1156
|
+
apiBase: "https://api.clerk.com",
|
|
1157
|
+
defaultDataClasses: ["pii"],
|
|
1158
|
+
sdks: {
|
|
1159
|
+
npm: ["@clerk/backend", "@clerk/nextjs"],
|
|
1160
|
+
pypi: ["clerk-backend-api"],
|
|
1161
|
+
go: ["github.com/clerk/clerk-sdk-go"]
|
|
1162
|
+
}
|
|
1163
|
+
},
|
|
1164
|
+
{
|
|
1165
|
+
id: "supabase",
|
|
1166
|
+
name: "Supabase",
|
|
1167
|
+
category: "Backend platform",
|
|
1168
|
+
hostSuffixes: ["supabase.co", "supabase.com"],
|
|
1169
|
+
apiBase: "https://api.supabase.com",
|
|
1170
|
+
defaultDataClasses: ["pii", "customer"],
|
|
1171
|
+
sdks: {
|
|
1172
|
+
npm: ["@supabase/supabase-js"],
|
|
1173
|
+
pypi: ["supabase"],
|
|
1174
|
+
cargo: ["postgrest"]
|
|
1175
|
+
}
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
id: "firebase",
|
|
1179
|
+
name: "Firebase",
|
|
1180
|
+
category: "Backend platform",
|
|
1181
|
+
hostSuffixes: ["firebaseio.com", "firebase.google.com"],
|
|
1182
|
+
apiBase: "https://firebaseio.com",
|
|
1183
|
+
defaultDataClasses: ["customer"],
|
|
1184
|
+
sdks: {
|
|
1185
|
+
npm: ["firebase", "firebase-admin"],
|
|
1186
|
+
pypi: ["firebase-admin"],
|
|
1187
|
+
go: ["firebase.google.com/go"],
|
|
1188
|
+
maven: ["com.google.firebase"]
|
|
1189
|
+
}
|
|
1190
|
+
},
|
|
1191
|
+
{
|
|
1192
|
+
id: "mongodb-atlas",
|
|
1193
|
+
name: "MongoDB Atlas",
|
|
1194
|
+
category: "Database SaaS",
|
|
1195
|
+
hostSuffixes: ["mongodb.net", "mongodb.com"],
|
|
1196
|
+
apiBase: "https://cloud.mongodb.com",
|
|
1197
|
+
defaultDataClasses: ["customer"],
|
|
1198
|
+
sdks: {
|
|
1199
|
+
npm: ["mongodb"],
|
|
1200
|
+
pypi: ["pymongo"],
|
|
1201
|
+
go: ["go.mongodb.org/mongo-driver"],
|
|
1202
|
+
maven: ["org.mongodb"],
|
|
1203
|
+
rubygems: ["mongo"],
|
|
1204
|
+
cargo: ["mongodb"],
|
|
1205
|
+
nuget: ["MongoDB.Driver"]
|
|
1206
|
+
}
|
|
1207
|
+
},
|
|
1208
|
+
{
|
|
1209
|
+
id: "planetscale",
|
|
1210
|
+
name: "PlanetScale",
|
|
1211
|
+
category: "Database SaaS",
|
|
1212
|
+
hostSuffixes: ["psdb.cloud", "planetscale.com"],
|
|
1213
|
+
apiBase: "https://api.planetscale.com",
|
|
1214
|
+
defaultDataClasses: ["customer"],
|
|
1215
|
+
sdks: {
|
|
1216
|
+
npm: ["@planetscale/database"],
|
|
1217
|
+
go: ["github.com/planetscale/planetscale-go"]
|
|
1218
|
+
}
|
|
1219
|
+
},
|
|
1220
|
+
{
|
|
1221
|
+
id: "algolia",
|
|
1222
|
+
name: "Algolia",
|
|
1223
|
+
category: "Search SaaS",
|
|
1224
|
+
hostSuffixes: ["algolia.net", "algolianet.com"],
|
|
1225
|
+
apiBase: "https://algolia.net",
|
|
1226
|
+
defaultDataClasses: ["customer"],
|
|
1227
|
+
sdks: {
|
|
1228
|
+
npm: ["algoliasearch"],
|
|
1229
|
+
pypi: ["algoliasearch"],
|
|
1230
|
+
go: ["github.com/algolia/algoliasearch-client-go"],
|
|
1231
|
+
maven: ["com.algolia"],
|
|
1232
|
+
rubygems: ["algolia"],
|
|
1233
|
+
composer: ["algolia/algoliasearch-client-php"],
|
|
1234
|
+
nuget: ["Algolia.Search"]
|
|
1235
|
+
}
|
|
1236
|
+
},
|
|
1237
|
+
{
|
|
1238
|
+
id: "cloudflare",
|
|
1239
|
+
name: "Cloudflare",
|
|
1240
|
+
category: "CDN / edge",
|
|
1241
|
+
hostSuffixes: ["cloudflare.com", "workers.dev"],
|
|
1242
|
+
apiBase: "https://api.cloudflare.com",
|
|
1243
|
+
defaultDataClasses: ["logs"],
|
|
1244
|
+
sdks: {
|
|
1245
|
+
npm: ["cloudflare"],
|
|
1246
|
+
pypi: ["cloudflare"],
|
|
1247
|
+
go: ["github.com/cloudflare/cloudflare-go"],
|
|
1248
|
+
nuget: ["CloudFlare.Client"]
|
|
1249
|
+
}
|
|
1250
|
+
},
|
|
1251
|
+
{
|
|
1252
|
+
id: "huggingface",
|
|
1253
|
+
name: "Hugging Face",
|
|
1254
|
+
category: "LLM provider",
|
|
1255
|
+
hostSuffixes: ["huggingface.co"],
|
|
1256
|
+
apiBase: "https://api-inference.huggingface.co",
|
|
1257
|
+
defaultDataClasses: ["source"],
|
|
1258
|
+
sdks: {
|
|
1259
|
+
npm: ["@huggingface/inference"],
|
|
1260
|
+
pypi: ["huggingface-hub", "transformers"],
|
|
1261
|
+
rubygems: ["hugging-face"]
|
|
1262
|
+
}
|
|
1263
|
+
},
|
|
1264
|
+
{
|
|
1265
|
+
id: "cohere",
|
|
1266
|
+
name: "Cohere",
|
|
1267
|
+
category: "LLM provider",
|
|
1268
|
+
hostSuffixes: ["cohere.com", "cohere.ai"],
|
|
1269
|
+
apiBase: "https://api.cohere.com",
|
|
1270
|
+
defaultDataClasses: ["pii", "source"],
|
|
1271
|
+
sdks: {
|
|
1272
|
+
npm: ["cohere-ai"],
|
|
1273
|
+
pypi: ["cohere"],
|
|
1274
|
+
go: ["github.com/cohere-ai/cohere-go"]
|
|
1275
|
+
}
|
|
1276
|
+
},
|
|
1277
|
+
{
|
|
1278
|
+
id: "mistral",
|
|
1279
|
+
name: "Mistral AI",
|
|
1280
|
+
category: "LLM provider",
|
|
1281
|
+
hostSuffixes: ["mistral.ai"],
|
|
1282
|
+
apiBase: "https://api.mistral.ai",
|
|
1283
|
+
defaultDataClasses: ["pii", "source"],
|
|
1284
|
+
sdks: {
|
|
1285
|
+
npm: ["@mistralai/mistralai"],
|
|
1286
|
+
pypi: ["mistralai"],
|
|
1287
|
+
go: ["github.com/gage-technologies/mistral-go"]
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
];
|
|
1291
|
+
var EGRESS_VERSION_MATERIAL = `${EXTRACTOR_VERSION}
|
|
1292
|
+
${JSON.stringify(PROVIDER_REGISTRY)}`;
|
|
1293
|
+
var INTERNAL_TLDS = ["internal", "local", "corp", "lan", "intranet", "home.arpa"];
|
|
1294
|
+
var EXCLUDED_HOST_SUFFIXES = [
|
|
1295
|
+
"localhost",
|
|
1296
|
+
"test",
|
|
1297
|
+
"example",
|
|
1298
|
+
"invalid",
|
|
1299
|
+
"example.com",
|
|
1300
|
+
"example.org",
|
|
1301
|
+
"example.net",
|
|
1302
|
+
"w3.org",
|
|
1303
|
+
"schemas.openxmlformats.org",
|
|
1304
|
+
"schemas.microsoft.com",
|
|
1305
|
+
"schemas.android.com",
|
|
1306
|
+
"maven.apache.org"
|
|
1307
|
+
];
|
|
1308
|
+
function hostMatchesSuffix(host, suffix) {
|
|
1309
|
+
return host === suffix || host.endsWith(`.${suffix}`);
|
|
1310
|
+
}
|
|
1311
|
+
function isValidIPv4(host) {
|
|
1312
|
+
const parts = host.split(".");
|
|
1313
|
+
if (parts.length !== 4) return false;
|
|
1314
|
+
return parts.every((part) => /^\d{1,3}$/.test(part) && Number(part) <= 255);
|
|
1315
|
+
}
|
|
1316
|
+
function isPrivateOrReservedIPv4(host) {
|
|
1317
|
+
const octets = host.split(".").map(Number);
|
|
1318
|
+
const a = octets[0];
|
|
1319
|
+
const b = octets[1];
|
|
1320
|
+
if (a === void 0 || b === void 0) return false;
|
|
1321
|
+
if (a === 0 || a === 10 || a === 127 || a >= 224) return true;
|
|
1322
|
+
if (a === 169 && b === 254) return true;
|
|
1323
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
1324
|
+
if (a === 192 && b === 168) return true;
|
|
1325
|
+
return false;
|
|
1326
|
+
}
|
|
1327
|
+
function normalizeIPv6Literal(host) {
|
|
1328
|
+
const unbracketed = host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
|
|
1329
|
+
const zoneIndex = unbracketed.indexOf("%");
|
|
1330
|
+
const unzoned = zoneIndex === -1 ? unbracketed : unbracketed.slice(0, zoneIndex);
|
|
1331
|
+
return unzoned.toLowerCase();
|
|
1332
|
+
}
|
|
1333
|
+
var IPV6_HEX_GROUP = /^[0-9a-f]{1,4}$/;
|
|
1334
|
+
function isValidIPv6(host) {
|
|
1335
|
+
const h = normalizeIPv6Literal(host);
|
|
1336
|
+
if (!h.includes(":")) return false;
|
|
1337
|
+
const collapsedHalves = h.split("::");
|
|
1338
|
+
if (collapsedHalves.length > 2) return false;
|
|
1339
|
+
const sides = collapsedHalves.length === 2 ? collapsedHalves : [h];
|
|
1340
|
+
const groups = sides.flatMap((side) => side === "" ? [] : side.split(":"));
|
|
1341
|
+
if (!groups.every((g) => IPV6_HEX_GROUP.test(g))) return false;
|
|
1342
|
+
return collapsedHalves.length === 2 ? groups.length <= 7 : groups.length === 8;
|
|
1343
|
+
}
|
|
1344
|
+
function isPrivateOrReservedIPv6(host) {
|
|
1345
|
+
const h = normalizeIPv6Literal(host);
|
|
1346
|
+
if (h === "::" || h === "::1") return true;
|
|
1347
|
+
const firstGroup = (h.split(":")[0] ?? "").padStart(4, "0");
|
|
1348
|
+
if (firstGroup.startsWith("fc") || firstGroup.startsWith("fd")) return true;
|
|
1349
|
+
if (firstGroup.startsWith("fe") && "89ab".includes(firstGroup[2] ?? "")) return true;
|
|
1350
|
+
if (firstGroup.startsWith("ff")) return true;
|
|
1351
|
+
return false;
|
|
1352
|
+
}
|
|
1353
|
+
var IPV4_MAPPED_IPV6 = /^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/;
|
|
1354
|
+
function ipv4MappedAddress(host) {
|
|
1355
|
+
const match = IPV4_MAPPED_IPV6.exec(normalizeIPv6Literal(host));
|
|
1356
|
+
return match?.[1] ?? null;
|
|
1357
|
+
}
|
|
1358
|
+
function resolveHost(host, opts) {
|
|
1359
|
+
const h = host.toLowerCase();
|
|
1360
|
+
if (isValidIPv4(h)) {
|
|
1361
|
+
if (isPrivateOrReservedIPv4(h)) return null;
|
|
1362
|
+
return { kind: "ip", trust: "ip", name: h, category: "Unresolved host", entry: null };
|
|
1363
|
+
}
|
|
1364
|
+
if (isValidIPv6(h)) {
|
|
1365
|
+
if (isPrivateOrReservedIPv6(h)) return null;
|
|
1366
|
+
return { kind: "ip", trust: "ip", name: h, category: "Unresolved host", entry: null };
|
|
1367
|
+
}
|
|
1368
|
+
const mappedIPv4 = ipv4MappedAddress(h);
|
|
1369
|
+
if (mappedIPv4 !== null && isValidIPv4(mappedIPv4)) {
|
|
1370
|
+
if (isPrivateOrReservedIPv4(mappedIPv4)) return null;
|
|
1371
|
+
return { kind: "ip", trust: "ip", name: h, category: "Unresolved host", entry: null };
|
|
1372
|
+
}
|
|
1373
|
+
if (EXCLUDED_HOST_SUFFIXES.some((suffix) => hostMatchesSuffix(h, suffix))) return null;
|
|
1374
|
+
const entry = PROVIDER_REGISTRY.find(
|
|
1375
|
+
(p) => p.hostSuffixes.some((suffix) => hostMatchesSuffix(h, suffix))
|
|
1376
|
+
);
|
|
1377
|
+
if (entry) {
|
|
1378
|
+
return {
|
|
1379
|
+
kind: "provider",
|
|
1380
|
+
trust: "recognized",
|
|
1381
|
+
name: entry.name,
|
|
1382
|
+
category: entry.category,
|
|
1383
|
+
entry
|
|
1384
|
+
};
|
|
1385
|
+
}
|
|
1386
|
+
const internalDomains = opts?.internalDomains ?? [];
|
|
1387
|
+
const isInternal = !h.includes(".") && !h.includes(":") || INTERNAL_TLDS.some((tld) => hostMatchesSuffix(h, tld)) || internalDomains.some((domain2) => hostMatchesSuffix(h, domain2.toLowerCase()));
|
|
1388
|
+
if (isInternal) {
|
|
1389
|
+
return {
|
|
1390
|
+
kind: "internal",
|
|
1391
|
+
trust: "internal",
|
|
1392
|
+
name: h,
|
|
1393
|
+
category: "Internal services",
|
|
1394
|
+
entry: null
|
|
1395
|
+
};
|
|
1396
|
+
}
|
|
1397
|
+
return {
|
|
1398
|
+
kind: "external",
|
|
1399
|
+
trust: "unverified",
|
|
1400
|
+
name: h,
|
|
1401
|
+
category: "External domain",
|
|
1402
|
+
entry: null
|
|
1403
|
+
};
|
|
1404
|
+
}
|
|
1405
|
+
function resolveSdk(ecosystem, pkg) {
|
|
1406
|
+
for (const entry of PROVIDER_REGISTRY) {
|
|
1407
|
+
const idents = entry.sdks[ecosystem];
|
|
1408
|
+
if (idents === void 0) continue;
|
|
1409
|
+
if (idents.some((ident) => sdkMatches(ecosystem, pkg, ident))) return entry;
|
|
1410
|
+
}
|
|
1411
|
+
return null;
|
|
1412
|
+
}
|
|
1413
|
+
function sdkMatches(ecosystem, pkg, ident) {
|
|
1414
|
+
switch (ecosystem) {
|
|
1415
|
+
case "nuget":
|
|
1416
|
+
return pkg.toLowerCase() === ident.toLowerCase();
|
|
1417
|
+
case "pypi":
|
|
1418
|
+
return normalizePypi(pkg) === normalizePypi(ident);
|
|
1419
|
+
case "go":
|
|
1420
|
+
return pkg === ident || pkg.startsWith(`${ident}/`);
|
|
1421
|
+
case "maven":
|
|
1422
|
+
return pkg === ident || pkg.startsWith(`${ident}.`);
|
|
1423
|
+
default:
|
|
1424
|
+
return pkg === ident;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
function normalizePypi(name) {
|
|
1428
|
+
return name.toLowerCase().replace(/[-_.]+/g, "-");
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
// ../packages/detections/src/egress/extract.ts
|
|
1432
|
+
var EGRESS_CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
1433
|
+
".ts",
|
|
1434
|
+
".tsx",
|
|
1435
|
+
".js",
|
|
1436
|
+
".jsx",
|
|
1437
|
+
".mjs",
|
|
1438
|
+
".cjs",
|
|
1439
|
+
".py",
|
|
1440
|
+
".java",
|
|
1441
|
+
".rb",
|
|
1442
|
+
".cs",
|
|
1443
|
+
".php",
|
|
1444
|
+
".go",
|
|
1445
|
+
".rs"
|
|
1446
|
+
]);
|
|
1447
|
+
var SNIPPET_MAX = 200;
|
|
1448
|
+
var MASK = "\u2022\u2022\u2022\u2022";
|
|
1449
|
+
var URL_CANDIDATE = /(https?|wss?|sftp|grpcs?|smtp):\/\/(?:[^\s'"`<>()[\]{},;]|\$\{[^}\s]{1,64}\}|\{[A-Za-z_]\w{0,63}\}|%[sd])+/gi;
|
|
1450
|
+
var PLACEHOLDER = /\$\{[^}\s]{1,64}\}|\{[A-Za-z_]\w{0,63}\}|%[sd]/g;
|
|
1451
|
+
var VAR_TOKEN = "${var}";
|
|
1452
|
+
var VAR_SENTINEL = "akaegressvar0";
|
|
1453
|
+
var TRAILING_PUNCTUATION = /[.,;:'"]+$/;
|
|
1454
|
+
var TRANSPORT_BY_SCHEME = {
|
|
1455
|
+
http: "http",
|
|
1456
|
+
https: "https",
|
|
1457
|
+
ws: "ws",
|
|
1458
|
+
wss: "wss",
|
|
1459
|
+
sftp: "sftp",
|
|
1460
|
+
grpc: "grpc",
|
|
1461
|
+
grpcs: "grpc",
|
|
1462
|
+
smtp: "smtp"
|
|
1463
|
+
};
|
|
1464
|
+
var BEFORE_WINDOW = 200;
|
|
1465
|
+
var AFTER_WINDOW = 300;
|
|
1466
|
+
var VERB_METHOD_OPENER = /\.\s*(get|post|put|delete)\s*\(\s*['"`]*$/i;
|
|
1467
|
+
var VERB_FIRST_ARGUMENT = /["'](GET|POST|PUT|DELETE)["']\s*,([^)\]};]{0,150})$/i;
|
|
1468
|
+
var OPTIONS_METHOD = /method\s*[:=]\s*['"](GET|POST|PUT|DELETE)/i;
|
|
1469
|
+
var CLIENT_OPENER = /\b(fetch|urlopen|got|ky)\s*\(\s*['"`]*$/i;
|
|
1470
|
+
var ANY_METHOD_KEY = /\bmethod\s*[:=]/i;
|
|
1471
|
+
var STATEMENT_BREAK = /;|\n\s*(?:(?:const|let|var|val|function|func|fn|def|class|return|export|import|if|for|while|switch|try|public|private)\b|\w+\s*=[^=])/;
|
|
1472
|
+
var IPV4_CANDIDATE = /\b(?:\d{1,3}\.){3}\d{1,3}(?::\d{1,5})?\b/g;
|
|
1473
|
+
var IP_HOST_CONTEXT = /\b(host|hostname|server|address|endpoint|ip|url|uri)\b/i;
|
|
1474
|
+
var IP_SFTP_CONTEXT = /\bs(ftp|cp|sh)\b/i;
|
|
1475
|
+
var IP_SMTP_CONTEXT = /\b(smtp|mail)\b/i;
|
|
1476
|
+
var SECRET_KEY_NAMES = "api[_-]?key|apikey|private[_-]?key|access[_-]?key|access[_-]?token|token|secret|credentials?|password|passwd|pwd|authorization|sig|signature|sas|assertion";
|
|
1477
|
+
var AUTH_SCHEMES = "Bearer|Basic|Token|Digest|ApiKey|SSWS|AWS4-HMAC-SHA256";
|
|
1478
|
+
var USERINFO = /:\/\/[^@/\s]+@/g;
|
|
1479
|
+
var SECRET_VALUE = new RegExp(
|
|
1480
|
+
`((?:${SECRET_KEY_NAMES})['"\`]?\\s*[:=]\\s*['"\`]?)(?!(?:${AUTH_SCHEMES})[\\s'"\`])[^\\s'"\`&]+`,
|
|
1481
|
+
"gi"
|
|
1482
|
+
);
|
|
1483
|
+
var AUTH_SCHEME_VALUE = new RegExp(
|
|
1484
|
+
`((?:${SECRET_KEY_NAMES})['"\`]?\\s*[:=]\\s*['"\`]?)(${AUTH_SCHEMES})\\s+[^\\s'"\`]+`,
|
|
1485
|
+
"gi"
|
|
1486
|
+
);
|
|
1487
|
+
var BEARER_TOKEN = /\bBearer\s+[A-Za-z0-9._~+/-]+=*/gi;
|
|
1488
|
+
var WEBHOOK_SECRET_PATHS = [
|
|
1489
|
+
{ hosts: ["hooks.slack.com"], prefix: "/services/" },
|
|
1490
|
+
{
|
|
1491
|
+
hosts: ["discord.com", "discordapp.com", "ptb.discord.com", "canary.discord.com"],
|
|
1492
|
+
prefix: "/api/webhooks/"
|
|
1493
|
+
},
|
|
1494
|
+
{ hosts: ["hooks.zapier.com"], prefix: "/hooks/" },
|
|
1495
|
+
{ hosts: ["outlook.office.com", "outlook.office365.com"], prefix: "/webhook/" }
|
|
1496
|
+
];
|
|
1497
|
+
function escapeRegExp(literal2) {
|
|
1498
|
+
return literal2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1499
|
+
}
|
|
1500
|
+
var WEBHOOK_URL = new RegExp(
|
|
1501
|
+
`(https?://(?:${WEBHOOK_SECRET_PATHS.flatMap(
|
|
1502
|
+
(entry) => entry.hosts.map((host) => `${escapeRegExp(host)}${escapeRegExp(entry.prefix)}`)
|
|
1503
|
+
).join("|")}))[^\\s'"\`<>()[\\]{},;]+`,
|
|
1504
|
+
"gi"
|
|
1505
|
+
);
|
|
1506
|
+
function maskWebhookPath(host, pathname) {
|
|
1507
|
+
for (const entry of WEBHOOK_SECRET_PATHS) {
|
|
1508
|
+
if (entry.hosts.includes(host) && pathname.startsWith(entry.prefix)) {
|
|
1509
|
+
return `${entry.prefix}${MASK}`;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
return pathname;
|
|
1513
|
+
}
|
|
1514
|
+
var VENDORED_PATH = /(^|\/)(vendor|third_party|external)\//;
|
|
1515
|
+
function isVendoredPath(file2) {
|
|
1516
|
+
return VENDORED_PATH.test(file2);
|
|
1517
|
+
}
|
|
1518
|
+
function redactLine(line) {
|
|
1519
|
+
return line.trim().replace(USERINFO, "://").replace(WEBHOOK_URL, `$1${MASK}`).replace(SECRET_VALUE, `$1${MASK}`).replace(AUTH_SCHEME_VALUE, `$1$2 ${MASK}`).replace(BEARER_TOKEN, `Bearer ${MASK}`);
|
|
1520
|
+
}
|
|
1521
|
+
function redactSnippet(line, anchor = 0) {
|
|
1522
|
+
const redacted = redactLine(line);
|
|
1523
|
+
if (redacted.length <= SNIPPET_MAX) return redacted;
|
|
1524
|
+
const lead = line.length - line.trimStart().length;
|
|
1525
|
+
const trimmed = line.trim();
|
|
1526
|
+
const mapped = redacted.length === trimmed.length ? anchor - lead : redactLine(trimmed.slice(0, Math.max(0, anchor - lead))).length;
|
|
1527
|
+
const start = Math.min(
|
|
1528
|
+
Math.max(0, mapped - Math.floor(SNIPPET_MAX / 2)),
|
|
1529
|
+
redacted.length - SNIPPET_MAX
|
|
1530
|
+
);
|
|
1531
|
+
return redacted.slice(start, start + SNIPPET_MAX);
|
|
1532
|
+
}
|
|
1533
|
+
function extractEgress(text) {
|
|
1534
|
+
const lineStarts = lineStartOffsets(text);
|
|
1535
|
+
const urlSpans = [];
|
|
1536
|
+
const hits = [];
|
|
1537
|
+
const lineTextOf = memoizeByLine((index) => lineTextAt(text, lineStarts, index));
|
|
1538
|
+
const ipContextOf = memoizeByLine((index) => ipLineContext(lineTextOf(index)));
|
|
1539
|
+
const snippetAt = (index, offset) => redactSnippet(lineTextOf(index), offset - (lineStarts[index] ?? 0));
|
|
1540
|
+
for (const match of text.matchAll(URL_CANDIDATE)) {
|
|
1541
|
+
const start = match.index;
|
|
1542
|
+
const matched = match[0];
|
|
1543
|
+
urlSpans.push([start, start + matched.length]);
|
|
1544
|
+
const scheme = match[1];
|
|
1545
|
+
if (scheme === void 0) continue;
|
|
1546
|
+
const candidate = matched.replace(TRAILING_PUNCTUATION, "");
|
|
1547
|
+
if (candidate === "") continue;
|
|
1548
|
+
const parsed = parseCandidate(candidate, scheme);
|
|
1549
|
+
if (parsed === null) continue;
|
|
1550
|
+
const index = lineIndexAt(lineStarts, start);
|
|
1551
|
+
hits.push({
|
|
1552
|
+
...parsed,
|
|
1553
|
+
method: inferMethod(text, start, start + matched.length),
|
|
1554
|
+
line: index + 1,
|
|
1555
|
+
snippet: snippetAt(index, start)
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
for (const match of text.matchAll(IPV4_CANDIDATE)) {
|
|
1559
|
+
const start = match.index;
|
|
1560
|
+
if (isInsideSpan(urlSpans, start)) continue;
|
|
1561
|
+
const index = lineIndexAt(lineStarts, start);
|
|
1562
|
+
const parsed = parseBareIp(match[0], ipContextOf(index));
|
|
1563
|
+
if (parsed === null) continue;
|
|
1564
|
+
hits.push({ ...parsed, method: "REF", line: index + 1, snippet: snippetAt(index, start) });
|
|
1565
|
+
}
|
|
1566
|
+
return hits.sort(compareHits);
|
|
1567
|
+
}
|
|
1568
|
+
function parseCandidate(candidate, scheme) {
|
|
1569
|
+
const transport = TRANSPORT_BY_SCHEME[scheme.toLowerCase()];
|
|
1570
|
+
if (transport === void 0) return null;
|
|
1571
|
+
let placeholders2 = 0;
|
|
1572
|
+
const normalized = candidate.replace(PLACEHOLDER, () => {
|
|
1573
|
+
placeholders2 += 1;
|
|
1574
|
+
return VAR_TOKEN;
|
|
1575
|
+
});
|
|
1576
|
+
let parsed;
|
|
1577
|
+
try {
|
|
1578
|
+
parsed = new URL(normalized.split(VAR_TOKEN).join(VAR_SENTINEL));
|
|
1579
|
+
} catch {
|
|
1580
|
+
return null;
|
|
1581
|
+
}
|
|
1582
|
+
const host = parsed.hostname.toLowerCase();
|
|
1583
|
+
if (host === "" || host.includes(VAR_SENTINEL)) return null;
|
|
1584
|
+
const authority = parsed.host.toLowerCase();
|
|
1585
|
+
const path = maskWebhookPath(host, parsed.pathname);
|
|
1586
|
+
const url2 = `${transport}://${authority}${path}`.split(VAR_SENTINEL).join(VAR_TOKEN);
|
|
1587
|
+
return {
|
|
1588
|
+
url: url2,
|
|
1589
|
+
host,
|
|
1590
|
+
port: parsed.port === "" ? null : Number(parsed.port),
|
|
1591
|
+
transport,
|
|
1592
|
+
template: placeholders2 > 0
|
|
1593
|
+
};
|
|
1594
|
+
}
|
|
1595
|
+
function parseBareIp(candidate, context) {
|
|
1596
|
+
const [address, port] = splitPort(candidate);
|
|
1597
|
+
if (!isValidIPv4(address) || isPrivateOrReservedIPv4(address)) return null;
|
|
1598
|
+
if (!context.named) return null;
|
|
1599
|
+
const { transport } = context;
|
|
1600
|
+
return {
|
|
1601
|
+
url: `${transport}://${address}${port === null ? "" : `:${String(port)}`}`,
|
|
1602
|
+
host: address,
|
|
1603
|
+
port,
|
|
1604
|
+
transport,
|
|
1605
|
+
template: false
|
|
1606
|
+
};
|
|
1607
|
+
}
|
|
1608
|
+
function ipLineContext(line) {
|
|
1609
|
+
const context = identifierWords(line);
|
|
1610
|
+
let transport = "http";
|
|
1611
|
+
if (IP_SFTP_CONTEXT.test(context)) transport = "sftp";
|
|
1612
|
+
else if (IP_SMTP_CONTEXT.test(context)) transport = "smtp";
|
|
1613
|
+
return { named: IP_HOST_CONTEXT.test(context), transport };
|
|
1614
|
+
}
|
|
1615
|
+
function isInsideSpan(spans, offset) {
|
|
1616
|
+
let low = 0;
|
|
1617
|
+
let high = spans.length - 1;
|
|
1618
|
+
let found = -1;
|
|
1619
|
+
while (low <= high) {
|
|
1620
|
+
const mid = Math.floor((low + high) / 2);
|
|
1621
|
+
const span2 = spans[mid];
|
|
1622
|
+
if (span2 === void 0) break;
|
|
1623
|
+
if (span2[0] <= offset) {
|
|
1624
|
+
found = mid;
|
|
1625
|
+
low = mid + 1;
|
|
1626
|
+
} else {
|
|
1627
|
+
high = mid - 1;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
const span = found === -1 ? void 0 : spans[found];
|
|
1631
|
+
return span !== void 0 && offset < span[1];
|
|
1632
|
+
}
|
|
1633
|
+
function splitPort(candidate) {
|
|
1634
|
+
const colon = candidate.indexOf(":");
|
|
1635
|
+
if (colon === -1) return [candidate, null];
|
|
1636
|
+
return [candidate.slice(0, colon), Number(candidate.slice(colon + 1))];
|
|
1637
|
+
}
|
|
1638
|
+
function identifierWords(line) {
|
|
1639
|
+
return line.replace(/[_-]+/g, " ").replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
1640
|
+
}
|
|
1641
|
+
function inferMethod(text, start, end) {
|
|
1642
|
+
const before = text.slice(Math.max(0, start - BEFORE_WINDOW), start);
|
|
1643
|
+
const after = text.slice(end, end + AFTER_WINDOW);
|
|
1644
|
+
const opener = VERB_METHOD_OPENER.exec(before);
|
|
1645
|
+
if (opener?.[1] !== void 0) return verbOf(opener[1]);
|
|
1646
|
+
const firstArgument = VERB_FIRST_ARGUMENT.exec(before);
|
|
1647
|
+
if (firstArgument?.[1] !== void 0 && !STATEMENT_BREAK.test(firstArgument[2] ?? "")) {
|
|
1648
|
+
return verbOf(firstArgument[1]);
|
|
1649
|
+
}
|
|
1650
|
+
const options = OPTIONS_METHOD.exec(after);
|
|
1651
|
+
if (options?.[1] !== void 0 && !STATEMENT_BREAK.test(after.slice(0, options.index))) {
|
|
1652
|
+
return verbOf(options[1]);
|
|
1653
|
+
}
|
|
1654
|
+
if (CLIENT_OPENER.test(before)) {
|
|
1655
|
+
const close = callCloseIndex(after);
|
|
1656
|
+
if (close !== -1 && isSoleArgument(after, close) && !ANY_METHOD_KEY.test(after.slice(0, close))) {
|
|
1657
|
+
return "GET";
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
return "REF";
|
|
1661
|
+
}
|
|
1662
|
+
function verbOf(raw) {
|
|
1663
|
+
return raw.toUpperCase();
|
|
1664
|
+
}
|
|
1665
|
+
function callCloseIndex(span) {
|
|
1666
|
+
let depth = 1;
|
|
1667
|
+
for (let i = 0; i < span.length; i += 1) {
|
|
1668
|
+
const char = span[i];
|
|
1669
|
+
if (char === "(") depth += 1;
|
|
1670
|
+
else if (char === ")") {
|
|
1671
|
+
depth -= 1;
|
|
1672
|
+
if (depth === 0) return i + 1;
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1675
|
+
return -1;
|
|
1676
|
+
}
|
|
1677
|
+
function isSoleArgument(span, close) {
|
|
1678
|
+
const between = span.slice(0, close - 1).trimEnd();
|
|
1679
|
+
const withoutTrailingComma = between.endsWith(",") ? between.slice(0, -1) : between;
|
|
1680
|
+
return !withoutTrailingComma.includes(",");
|
|
1681
|
+
}
|
|
1682
|
+
function memoizeByLine(compute) {
|
|
1683
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1684
|
+
return (index) => {
|
|
1685
|
+
const cached2 = cache.get(index);
|
|
1686
|
+
if (cached2 !== void 0) return cached2;
|
|
1687
|
+
const value = compute(index);
|
|
1688
|
+
cache.set(index, value);
|
|
1689
|
+
return value;
|
|
1690
|
+
};
|
|
1691
|
+
}
|
|
1692
|
+
function lineStartOffsets(text) {
|
|
1693
|
+
const starts = [0];
|
|
1694
|
+
for (let i = 0; i < text.length; i += 1) {
|
|
1695
|
+
if (text[i] === "\n") starts.push(i + 1);
|
|
1696
|
+
}
|
|
1697
|
+
return starts;
|
|
1698
|
+
}
|
|
1699
|
+
function lineIndexAt(starts, offset) {
|
|
1700
|
+
let low = 0;
|
|
1701
|
+
let high = starts.length - 1;
|
|
1702
|
+
while (low < high) {
|
|
1703
|
+
const mid = Math.ceil((low + high) / 2);
|
|
1704
|
+
if ((starts[mid] ?? 0) <= offset) low = mid;
|
|
1705
|
+
else high = mid - 1;
|
|
1706
|
+
}
|
|
1707
|
+
return low;
|
|
1708
|
+
}
|
|
1709
|
+
function lineTextAt(text, starts, index) {
|
|
1710
|
+
const start = starts[index] ?? 0;
|
|
1711
|
+
const next = starts[index + 1];
|
|
1712
|
+
return next === void 0 ? text.slice(start) : text.slice(start, next - 1);
|
|
1713
|
+
}
|
|
1714
|
+
function compareHits(a, b) {
|
|
1715
|
+
if (a.line !== b.line) return a.line - b.line;
|
|
1716
|
+
if (a.url !== b.url) return a.url < b.url ? -1 : 1;
|
|
1717
|
+
return 0;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
// ../packages/detections/src/egress/manifests.ts
|
|
1721
|
+
var LOCKFILE_BASENAMES = /* @__PURE__ */ new Set([
|
|
1722
|
+
"package-lock.json",
|
|
1723
|
+
"yarn.lock",
|
|
1724
|
+
"pnpm-lock.yaml",
|
|
1725
|
+
"Cargo.lock",
|
|
1726
|
+
"composer.lock",
|
|
1727
|
+
"Gemfile.lock",
|
|
1728
|
+
"poetry.lock",
|
|
1729
|
+
"go.sum",
|
|
1730
|
+
"packages.lock.json"
|
|
1731
|
+
]);
|
|
1732
|
+
var EXACT_KIND_BY_BASENAME = {
|
|
1733
|
+
"package.json": "package.json",
|
|
1734
|
+
"requirements.txt": "requirements.txt",
|
|
1735
|
+
"pyproject.toml": "pyproject.toml",
|
|
1736
|
+
"go.mod": "go.mod",
|
|
1737
|
+
"pom.xml": "pom.xml",
|
|
1738
|
+
"build.gradle": "build.gradle",
|
|
1739
|
+
"build.gradle.kts": "build.gradle",
|
|
1740
|
+
Gemfile: "Gemfile",
|
|
1741
|
+
"Cargo.toml": "Cargo.toml",
|
|
1742
|
+
"composer.json": "composer.json",
|
|
1743
|
+
"packages.config": "packages.config"
|
|
1744
|
+
};
|
|
1745
|
+
function manifestKindOf(basename6) {
|
|
1746
|
+
if (LOCKFILE_BASENAMES.has(basename6)) return null;
|
|
1747
|
+
const exact = Object.hasOwn(EXACT_KIND_BY_BASENAME, basename6) ? EXACT_KIND_BY_BASENAME[basename6] : void 0;
|
|
1748
|
+
if (exact !== void 0) return exact;
|
|
1749
|
+
if (basename6.endsWith(".csproj")) return "csproj";
|
|
1750
|
+
return null;
|
|
1751
|
+
}
|
|
1752
|
+
function extractManifestSdks(text, kind) {
|
|
1753
|
+
switch (kind) {
|
|
1754
|
+
case "package.json":
|
|
1755
|
+
return extractPackageJson(text);
|
|
1756
|
+
case "requirements.txt":
|
|
1757
|
+
return extractRequirementsTxt(text);
|
|
1758
|
+
case "pyproject.toml":
|
|
1759
|
+
return extractPyprojectToml(text);
|
|
1760
|
+
case "go.mod":
|
|
1761
|
+
return extractGoMod(text);
|
|
1762
|
+
case "pom.xml":
|
|
1763
|
+
return extractPomXml(text);
|
|
1764
|
+
case "build.gradle":
|
|
1765
|
+
return extractBuildGradle(text);
|
|
1766
|
+
case "Gemfile":
|
|
1767
|
+
return extractGemfile(text);
|
|
1768
|
+
case "Cargo.toml":
|
|
1769
|
+
return extractCargoToml(text);
|
|
1770
|
+
case "composer.json":
|
|
1771
|
+
return extractComposerJson(text);
|
|
1772
|
+
case "csproj":
|
|
1773
|
+
return extractCsproj(text);
|
|
1774
|
+
case "packages.config":
|
|
1775
|
+
return extractPackagesConfig(text);
|
|
1776
|
+
default:
|
|
1777
|
+
return [];
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
function makeHit(ecosystem, pkg, line, rawLine) {
|
|
1781
|
+
return { ecosystem, pkg, line, snippet: redactSnippet(rawLine) };
|
|
1782
|
+
}
|
|
1783
|
+
function extractPackageJson(text) {
|
|
1784
|
+
const parsed = parseJson(text);
|
|
1785
|
+
if (parsed === null) return [];
|
|
1786
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1787
|
+
const hits = [];
|
|
1788
|
+
for (const pkg of objectKeys(parsed.dependencies)) {
|
|
1789
|
+
seen.add(pkg);
|
|
1790
|
+
hits.push(hitAtQuotedKey("npm", pkg, text, "dependencies"));
|
|
1791
|
+
}
|
|
1792
|
+
for (const pkg of objectKeys(parsed.optionalDependencies)) {
|
|
1793
|
+
if (seen.has(pkg)) continue;
|
|
1794
|
+
seen.add(pkg);
|
|
1795
|
+
hits.push(hitAtQuotedKey("npm", pkg, text, "optionalDependencies"));
|
|
1796
|
+
}
|
|
1797
|
+
return hits;
|
|
1798
|
+
}
|
|
1799
|
+
var REQUIREMENTS_NAME = /^\s*([A-Za-z0-9][\w.-]*)/;
|
|
1800
|
+
function extractRequirementsTxt(text) {
|
|
1801
|
+
const hits = [];
|
|
1802
|
+
eachLine(text, (rawLine, lineNumber) => {
|
|
1803
|
+
const match = REQUIREMENTS_NAME.exec(rawLine);
|
|
1804
|
+
const name = match?.[1];
|
|
1805
|
+
if (name === void 0) return;
|
|
1806
|
+
hits.push(makeHit("pypi", normalizePypi(name), lineNumber, rawLine));
|
|
1807
|
+
});
|
|
1808
|
+
return hits;
|
|
1809
|
+
}
|
|
1810
|
+
var TOML_SECTION = /^\s*\[([^\]]+)\]\s*$/;
|
|
1811
|
+
var TOML_QUOTED = /"([^"]*)"|'([^']*)'/g;
|
|
1812
|
+
var PEP508_NAME = /^([A-Za-z0-9][\w.-]*)/;
|
|
1813
|
+
var POETRY_KEY = /^\s*([A-Za-z0-9][\w.-]*)\s*=/;
|
|
1814
|
+
function extractPyprojectToml(text) {
|
|
1815
|
+
const hits = [];
|
|
1816
|
+
let section = "";
|
|
1817
|
+
let inDependenciesArray = false;
|
|
1818
|
+
eachLine(text, (rawLine, lineNumber) => {
|
|
1819
|
+
const sectionMatch = TOML_SECTION.exec(rawLine);
|
|
1820
|
+
if (sectionMatch) {
|
|
1821
|
+
section = sectionMatch[1]?.trim() ?? "";
|
|
1822
|
+
inDependenciesArray = false;
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
if (!inDependenciesArray && /^\s*dependencies\s*=\s*\[/.test(rawLine)) {
|
|
1826
|
+
inDependenciesArray = true;
|
|
1827
|
+
}
|
|
1828
|
+
if (inDependenciesArray) {
|
|
1829
|
+
for (const spec of quotedStrings(rawLine)) {
|
|
1830
|
+
const name = PEP508_NAME.exec(spec)?.[1];
|
|
1831
|
+
if (name !== void 0)
|
|
1832
|
+
hits.push(makeHit("pypi", normalizePypi(name), lineNumber, rawLine));
|
|
1833
|
+
}
|
|
1834
|
+
if (rawLine.includes("]")) inDependenciesArray = false;
|
|
1835
|
+
return;
|
|
1836
|
+
}
|
|
1837
|
+
if (section === "tool.poetry.dependencies") {
|
|
1838
|
+
const key = POETRY_KEY.exec(rawLine)?.[1];
|
|
1839
|
+
if (key !== void 0 && key !== "python") {
|
|
1840
|
+
hits.push(makeHit("pypi", normalizePypi(key), lineNumber, rawLine));
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
});
|
|
1844
|
+
return hits;
|
|
1845
|
+
}
|
|
1846
|
+
function quotedStrings(line) {
|
|
1847
|
+
return [...line.matchAll(TOML_QUOTED)].map((m) => m[1] ?? m[2] ?? "");
|
|
1848
|
+
}
|
|
1849
|
+
var GO_BLOCK_OPEN = /^\s*(require|exclude|replace|retract)\s*\(/;
|
|
1850
|
+
var GO_BLOCK_CLOSE = /^\s*\)/;
|
|
1851
|
+
var GO_REQUIRE_SINGLE_LINE = /^\s*require\s+([\w./-]+)\s+v\d/;
|
|
1852
|
+
var GO_MODULE_VERSION_LINE = /^\s*([\w./-]+)\s+v\d/;
|
|
1853
|
+
function extractGoMod(text) {
|
|
1854
|
+
const hits = [];
|
|
1855
|
+
let blockKeyword = null;
|
|
1856
|
+
eachLine(text, (rawLine, lineNumber) => {
|
|
1857
|
+
if (blockKeyword === null) {
|
|
1858
|
+
const open = GO_BLOCK_OPEN.exec(rawLine)?.[1];
|
|
1859
|
+
if (open !== void 0) {
|
|
1860
|
+
blockKeyword = open;
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1863
|
+
const path = GO_REQUIRE_SINGLE_LINE.exec(rawLine)?.[1];
|
|
1864
|
+
if (path !== void 0) hits.push(makeHit("go", path, lineNumber, rawLine));
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
1867
|
+
if (GO_BLOCK_CLOSE.test(rawLine)) {
|
|
1868
|
+
blockKeyword = null;
|
|
1869
|
+
return;
|
|
1870
|
+
}
|
|
1871
|
+
if (blockKeyword === "require") {
|
|
1872
|
+
const path = GO_MODULE_VERSION_LINE.exec(rawLine)?.[1];
|
|
1873
|
+
if (path !== void 0) hits.push(makeHit("go", path, lineNumber, rawLine));
|
|
1874
|
+
}
|
|
1875
|
+
});
|
|
1876
|
+
return hits;
|
|
1877
|
+
}
|
|
1878
|
+
var POM_CONTEXT_TAGS = /* @__PURE__ */ new Set([
|
|
1879
|
+
"project",
|
|
1880
|
+
"parent",
|
|
1881
|
+
"dependencyManagement",
|
|
1882
|
+
"dependencies",
|
|
1883
|
+
"dependency",
|
|
1884
|
+
"build",
|
|
1885
|
+
"plugins",
|
|
1886
|
+
"plugin",
|
|
1887
|
+
"exclusions",
|
|
1888
|
+
"exclusion"
|
|
1889
|
+
]);
|
|
1890
|
+
var XML_TAG = /<(\/?)([A-Za-z][\w.-]*)([^<>]*)>/g;
|
|
1891
|
+
var LEADING_TEXT = /^([^<]*)/;
|
|
1892
|
+
function extractPomXml(text) {
|
|
1893
|
+
const hits = [];
|
|
1894
|
+
const stack = [];
|
|
1895
|
+
let inComment = false;
|
|
1896
|
+
eachLine(text, (rawLine, lineNumber) => {
|
|
1897
|
+
const stripped = stripXmlComments(rawLine, inComment);
|
|
1898
|
+
inComment = stripped.inComment;
|
|
1899
|
+
const visible = stripped.visible;
|
|
1900
|
+
for (const match of visible.matchAll(XML_TAG)) {
|
|
1901
|
+
const name = match[2];
|
|
1902
|
+
if (name === void 0) continue;
|
|
1903
|
+
const closing = match[1] === "/";
|
|
1904
|
+
const rest = match[3] ?? "";
|
|
1905
|
+
const selfClosing = rest.trimEnd().endsWith("/");
|
|
1906
|
+
if (closing) {
|
|
1907
|
+
if (POM_CONTEXT_TAGS.has(name) && stack[stack.length - 1] === name) stack.pop();
|
|
1908
|
+
continue;
|
|
1909
|
+
}
|
|
1910
|
+
if (selfClosing) continue;
|
|
1911
|
+
if (name === "groupId") {
|
|
1912
|
+
const after = visible.slice(match.index + match[0].length);
|
|
1913
|
+
const value = LEADING_TEXT.exec(after)?.[1]?.trim() ?? "";
|
|
1914
|
+
if (value !== "" && isProjectDependencyGroupId(stack)) {
|
|
1915
|
+
hits.push(makeHit("maven", value, lineNumber, rawLine));
|
|
1916
|
+
}
|
|
1917
|
+
continue;
|
|
1918
|
+
}
|
|
1919
|
+
if (POM_CONTEXT_TAGS.has(name)) stack.push(name);
|
|
1920
|
+
}
|
|
1921
|
+
});
|
|
1922
|
+
return hits;
|
|
1923
|
+
}
|
|
1924
|
+
function isProjectDependencyGroupId(stack) {
|
|
1925
|
+
return stack[stack.length - 1] === "dependency" && stack[stack.length - 2] === "dependencies" && !stack.includes("dependencyManagement") && !stack.includes("parent") && !stack.includes("build");
|
|
1926
|
+
}
|
|
1927
|
+
var GRADLE_DEPENDENCY = /\b(?:implementation|api|compile)\b\s*[('"]*(?:platform\s*\(\s*['"])?([\w.-]+):[\w.-]+/g;
|
|
1928
|
+
var LINE_COMMENT = /^\s*\/\//;
|
|
1929
|
+
function extractBuildGradle(text) {
|
|
1930
|
+
const hits = [];
|
|
1931
|
+
eachLine(text, (rawLine, lineNumber) => {
|
|
1932
|
+
if (LINE_COMMENT.test(rawLine)) return;
|
|
1933
|
+
for (const match of rawLine.matchAll(GRADLE_DEPENDENCY)) {
|
|
1934
|
+
const groupId = match[1];
|
|
1935
|
+
if (groupId !== void 0) hits.push(makeHit("maven", groupId, lineNumber, rawLine));
|
|
1936
|
+
}
|
|
1937
|
+
});
|
|
1938
|
+
return hits;
|
|
1939
|
+
}
|
|
1940
|
+
var GEMFILE_DEPENDENCY = /^\s*gem\s+['"]([\w-]+)['"]/;
|
|
1941
|
+
function extractGemfile(text) {
|
|
1942
|
+
const hits = [];
|
|
1943
|
+
eachLine(text, (rawLine, lineNumber) => {
|
|
1944
|
+
const name = GEMFILE_DEPENDENCY.exec(rawLine)?.[1];
|
|
1945
|
+
if (name !== void 0) hits.push(makeHit("rubygems", name, lineNumber, rawLine));
|
|
1946
|
+
});
|
|
1947
|
+
return hits;
|
|
1948
|
+
}
|
|
1949
|
+
var CARGO_KEY = /^([A-Za-z0-9_-]+)\s*=/;
|
|
1950
|
+
function extractCargoToml(text) {
|
|
1951
|
+
const hits = [];
|
|
1952
|
+
let mode = "none";
|
|
1953
|
+
eachLine(text, (rawLine, lineNumber) => {
|
|
1954
|
+
const sectionMatch = TOML_SECTION.exec(rawLine);
|
|
1955
|
+
if (sectionMatch) {
|
|
1956
|
+
const name = sectionMatch[1]?.trim() ?? "";
|
|
1957
|
+
if (name === "dependencies") {
|
|
1958
|
+
mode = "plain";
|
|
1959
|
+
} else if (name.startsWith("dependencies.")) {
|
|
1960
|
+
mode = "dotted";
|
|
1961
|
+
const crate = name.slice("dependencies.".length);
|
|
1962
|
+
if (crate !== "") hits.push(makeHit("cargo", crate, lineNumber, rawLine));
|
|
1963
|
+
} else {
|
|
1964
|
+
mode = "none";
|
|
1965
|
+
}
|
|
1966
|
+
return;
|
|
1967
|
+
}
|
|
1968
|
+
if (mode === "plain") {
|
|
1969
|
+
const crate = CARGO_KEY.exec(rawLine)?.[1];
|
|
1970
|
+
if (crate !== void 0) hits.push(makeHit("cargo", crate, lineNumber, rawLine));
|
|
1971
|
+
}
|
|
1972
|
+
});
|
|
1973
|
+
return hits;
|
|
1974
|
+
}
|
|
1975
|
+
function extractComposerJson(text) {
|
|
1976
|
+
const parsed = parseJson(text);
|
|
1977
|
+
if (parsed === null) return [];
|
|
1978
|
+
const pkgs = objectKeys(parsed.require).filter((pkg) => pkg !== "php" && !pkg.startsWith("ext-"));
|
|
1979
|
+
return pkgs.map((pkg) => hitAtQuotedKey("composer", pkg, text, "require"));
|
|
1980
|
+
}
|
|
1981
|
+
var CSPROJ_PACKAGE_REFERENCE = /<PackageReference\s+Include="([^"]+)"/;
|
|
1982
|
+
function extractCsproj(text) {
|
|
1983
|
+
const hits = [];
|
|
1984
|
+
let inComment = false;
|
|
1985
|
+
eachLine(text, (rawLine, lineNumber) => {
|
|
1986
|
+
const stripped = stripXmlComments(rawLine, inComment);
|
|
1987
|
+
inComment = stripped.inComment;
|
|
1988
|
+
const name = CSPROJ_PACKAGE_REFERENCE.exec(stripped.visible)?.[1];
|
|
1989
|
+
if (name !== void 0) hits.push(makeHit("nuget", name, lineNumber, rawLine));
|
|
1990
|
+
});
|
|
1991
|
+
return hits;
|
|
1992
|
+
}
|
|
1993
|
+
var PACKAGES_CONFIG_PACKAGE = /<package\s+id="([^"]+)"/;
|
|
1994
|
+
function extractPackagesConfig(text) {
|
|
1995
|
+
const hits = [];
|
|
1996
|
+
let inComment = false;
|
|
1997
|
+
eachLine(text, (rawLine, lineNumber) => {
|
|
1998
|
+
const stripped = stripXmlComments(rawLine, inComment);
|
|
1999
|
+
inComment = stripped.inComment;
|
|
2000
|
+
const name = PACKAGES_CONFIG_PACKAGE.exec(stripped.visible)?.[1];
|
|
2001
|
+
if (name !== void 0) hits.push(makeHit("nuget", name, lineNumber, rawLine));
|
|
2002
|
+
});
|
|
2003
|
+
return hits;
|
|
2004
|
+
}
|
|
2005
|
+
function stripXmlComments(line, inComment) {
|
|
2006
|
+
let visible = "";
|
|
2007
|
+
let rest = line;
|
|
2008
|
+
let comment = inComment;
|
|
2009
|
+
for (; ; ) {
|
|
2010
|
+
if (comment) {
|
|
2011
|
+
const end = rest.indexOf("-->");
|
|
2012
|
+
if (end === -1) return { visible, inComment: true };
|
|
2013
|
+
rest = rest.slice(end + 3);
|
|
2014
|
+
comment = false;
|
|
2015
|
+
continue;
|
|
2016
|
+
}
|
|
2017
|
+
const start = rest.indexOf("<!--");
|
|
2018
|
+
if (start === -1) return { visible: visible + rest, inComment: false };
|
|
2019
|
+
visible += rest.slice(0, start);
|
|
2020
|
+
rest = rest.slice(start + 4);
|
|
2021
|
+
comment = true;
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
function eachLine(text, fn) {
|
|
2025
|
+
const lines = text.split("\n");
|
|
2026
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
2027
|
+
fn(lines[i] ?? "", i + 1);
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
function parseJson(text) {
|
|
2031
|
+
let parsed;
|
|
2032
|
+
try {
|
|
2033
|
+
parsed = JSON.parse(text);
|
|
2034
|
+
} catch {
|
|
2035
|
+
return null;
|
|
2036
|
+
}
|
|
2037
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
2038
|
+
const record2 = parsed;
|
|
2039
|
+
return {
|
|
2040
|
+
dependencies: record2.dependencies,
|
|
2041
|
+
optionalDependencies: record2.optionalDependencies,
|
|
2042
|
+
require: record2.require
|
|
2043
|
+
};
|
|
2044
|
+
}
|
|
2045
|
+
function objectKeys(value) {
|
|
2046
|
+
if (typeof value !== "object" || value === null) return [];
|
|
2047
|
+
return Object.keys(value);
|
|
2048
|
+
}
|
|
2049
|
+
function hitAtQuotedKey(ecosystem, pkg, text, sectionKey) {
|
|
2050
|
+
const sectionStart = text.indexOf(`"${sectionKey}"`);
|
|
2051
|
+
const searchFrom = sectionStart === -1 ? 0 : sectionStart;
|
|
2052
|
+
const index = text.indexOf(`"${pkg}"`, searchFrom);
|
|
2053
|
+
if (index === -1) return makeHit(ecosystem, pkg, 1, pkg);
|
|
2054
|
+
return makeHit(ecosystem, pkg, lineNumberAt(text, index), lineContaining(text, index));
|
|
2055
|
+
}
|
|
2056
|
+
function lineNumberAt(text, index) {
|
|
2057
|
+
let line = 1;
|
|
2058
|
+
for (let i = 0; i < index; i += 1) {
|
|
2059
|
+
if (text[i] === "\n") line += 1;
|
|
2060
|
+
}
|
|
2061
|
+
return line;
|
|
2062
|
+
}
|
|
2063
|
+
function lineContaining(text, index) {
|
|
2064
|
+
const start = text.lastIndexOf("\n", index) + 1;
|
|
2065
|
+
const end = text.indexOf("\n", index);
|
|
2066
|
+
return text.slice(start, end === -1 ? text.length : end);
|
|
2067
|
+
}
|
|
2068
|
+
|
|
2069
|
+
// ../packages/detections/src/egress/resolve.ts
|
|
2070
|
+
var VERB_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "DELETE"]);
|
|
2071
|
+
function resolveEgress(files, opts) {
|
|
2072
|
+
const resolved = [];
|
|
2073
|
+
for (const file2 of files) {
|
|
2074
|
+
for (const endpoint of file2.endpoints) {
|
|
2075
|
+
const hit = resolveEndpointHit(file2, endpoint, opts);
|
|
2076
|
+
if (hit !== null) resolved.push(hit);
|
|
2077
|
+
}
|
|
2078
|
+
for (const sdkHit of file2.sdkHits) {
|
|
2079
|
+
const hit = resolveSdkHit(file2, sdkHit);
|
|
2080
|
+
if (hit !== null) resolved.push(hit);
|
|
2081
|
+
}
|
|
2082
|
+
}
|
|
2083
|
+
return dropRefsWithVerbSibling(dedupeExact(resolved));
|
|
2084
|
+
}
|
|
2085
|
+
function resolveEndpointHit(file2, endpoint, opts) {
|
|
2086
|
+
const resolution = resolveHost(endpoint.host, opts);
|
|
2087
|
+
if (resolution === null) return null;
|
|
2088
|
+
const isProvider = resolution.kind === "provider";
|
|
2089
|
+
return {
|
|
2090
|
+
host: endpoint.host,
|
|
2091
|
+
kind: resolution.kind,
|
|
2092
|
+
name: resolution.name,
|
|
2093
|
+
category: resolution.category,
|
|
2094
|
+
trust: resolution.trust,
|
|
2095
|
+
network: isProvider ? null : { port: endpoint.port, geo: null, ptr: null },
|
|
2096
|
+
method: endpoint.method,
|
|
2097
|
+
transport: endpoint.transport,
|
|
2098
|
+
url: endpoint.url,
|
|
2099
|
+
template: endpoint.template,
|
|
2100
|
+
dataClass: isProvider ? resolution.entry?.defaultDataClasses[0] ?? "none" : "none",
|
|
2101
|
+
site: {
|
|
2102
|
+
file: file2.file,
|
|
2103
|
+
line: endpoint.line,
|
|
2104
|
+
snippet: endpoint.snippet,
|
|
2105
|
+
dynamic: endpoint.template,
|
|
2106
|
+
vendored: file2.vendored
|
|
2107
|
+
}
|
|
2108
|
+
};
|
|
2109
|
+
}
|
|
2110
|
+
function resolveSdkHit(file2, sdkHit) {
|
|
2111
|
+
const entry = resolveSdk(sdkHit.ecosystem, sdkHit.pkg);
|
|
2112
|
+
if (entry === null) return null;
|
|
2113
|
+
return {
|
|
2114
|
+
host: new URL(entry.apiBase).hostname,
|
|
2115
|
+
kind: "provider",
|
|
2116
|
+
name: entry.name,
|
|
2117
|
+
category: entry.category,
|
|
2118
|
+
trust: "recognized",
|
|
2119
|
+
network: null,
|
|
2120
|
+
method: "SDK",
|
|
2121
|
+
transport: "https",
|
|
2122
|
+
url: entry.apiBase,
|
|
2123
|
+
template: false,
|
|
2124
|
+
dataClass: entry.defaultDataClasses[0] ?? "none",
|
|
2125
|
+
site: {
|
|
2126
|
+
file: file2.file,
|
|
2127
|
+
line: sdkHit.line,
|
|
2128
|
+
snippet: sdkHit.snippet,
|
|
2129
|
+
dynamic: false,
|
|
2130
|
+
vendored: file2.vendored
|
|
2131
|
+
}
|
|
2132
|
+
};
|
|
2133
|
+
}
|
|
2134
|
+
function dedupeExact(hits) {
|
|
2135
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2136
|
+
const deduped = [];
|
|
2137
|
+
for (const hit of hits) {
|
|
2138
|
+
const key = exactKey(hit);
|
|
2139
|
+
if (seen.has(key)) continue;
|
|
2140
|
+
seen.add(key);
|
|
2141
|
+
deduped.push(hit);
|
|
2142
|
+
}
|
|
2143
|
+
return deduped;
|
|
2144
|
+
}
|
|
2145
|
+
function exactKey(hit) {
|
|
2146
|
+
return JSON.stringify([hit.host, hit.method, hit.url, hit.site.file, hit.site.line]);
|
|
2147
|
+
}
|
|
2148
|
+
function dropRefsWithVerbSibling(hits) {
|
|
2149
|
+
const verbPairs = /* @__PURE__ */ new Set();
|
|
2150
|
+
for (const hit of hits) {
|
|
2151
|
+
if (VERB_METHODS.has(hit.method)) verbPairs.add(pairKey(hit));
|
|
2152
|
+
}
|
|
2153
|
+
return hits.filter((hit) => hit.method !== "REF" || !verbPairs.has(pairKey(hit)));
|
|
2154
|
+
}
|
|
2155
|
+
function pairKey(hit) {
|
|
2156
|
+
return JSON.stringify([hit.host, hit.url]);
|
|
2157
|
+
}
|
|
757
2158
|
|
|
758
2159
|
// ../packages/detections/src/escape-regexp.ts
|
|
759
|
-
function
|
|
2160
|
+
function escapeRegExp2(value) {
|
|
760
2161
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
761
2162
|
}
|
|
762
2163
|
|
|
763
2164
|
// ../packages/detections/src/matchers/limits.ts
|
|
764
2165
|
var MAX_MATCHES_PER_RULE = 1e4;
|
|
2166
|
+
var MAX_REGEX_INPUT_LENGTH = 2e5;
|
|
765
2167
|
|
|
766
2168
|
// ../packages/detections/src/matchers/keyword.ts
|
|
767
2169
|
var KeywordMatcher = class {
|
|
@@ -772,7 +2174,7 @@ var KeywordMatcher = class {
|
|
|
772
2174
|
for (const kw of keywords) {
|
|
773
2175
|
if (kw.length === 0) continue;
|
|
774
2176
|
if (spans.length >= MAX_MATCHES_PER_RULE) break;
|
|
775
|
-
const re = new RegExp(
|
|
2177
|
+
const re = new RegExp(escapeRegExp2(kw), caseSensitive ? "gu" : "giu");
|
|
776
2178
|
let m;
|
|
777
2179
|
while ((m = re.exec(text)) !== null) {
|
|
778
2180
|
spans.push({ start: m.index, end: m.index + m[0].length });
|
|
@@ -789,9 +2191,13 @@ var RegexMatcher = class {
|
|
|
789
2191
|
if (rule.matcher.type !== "regex") return [];
|
|
790
2192
|
const { pattern, flags, captureGroup } = rule.matcher;
|
|
791
2193
|
const re = new RegExp(pattern, flags.includes("d") ? flags : `${flags}d`);
|
|
2194
|
+
const scanText2 = text.length > MAX_REGEX_INPUT_LENGTH ? text.slice(0, MAX_REGEX_INPUT_LENGTH) : text;
|
|
792
2195
|
const spans = [];
|
|
793
2196
|
let m;
|
|
794
|
-
|
|
2197
|
+
const maxIterations = scanText2.length + 1;
|
|
2198
|
+
let iterations = 0;
|
|
2199
|
+
while ((m = re.exec(scanText2)) !== null) {
|
|
2200
|
+
if (++iterations > maxIterations) break;
|
|
795
2201
|
const group = captureGroup != null ? m[captureGroup] : m[0];
|
|
796
2202
|
if (m[0].length === 0) re.lastIndex++;
|
|
797
2203
|
if (group && spans.length < MAX_MATCHES_PER_RULE) {
|
|
@@ -895,7 +2301,7 @@ function isCorroborated(candidate, candidates, text) {
|
|
|
895
2301
|
for (const label of labels) {
|
|
896
2302
|
const trimmed = label.trim();
|
|
897
2303
|
if (trimmed.length === 0) continue;
|
|
898
|
-
const re = new RegExp(`(?<![A-Za-z0-9])${
|
|
2304
|
+
const re = new RegExp(`(?<![A-Za-z0-9])${escapeRegExp2(trimmed)}(?![A-Za-z0-9])`, "i");
|
|
899
2305
|
if (re.test(haystack)) return true;
|
|
900
2306
|
}
|
|
901
2307
|
}
|
|
@@ -1060,6 +2466,37 @@ var CONFIG_POSTURE_RULES = [
|
|
|
1060
2466
|
}
|
|
1061
2467
|
];
|
|
1062
2468
|
|
|
2469
|
+
// ../packages/detections/src/security/redos-probe.ts
|
|
2470
|
+
var EXPONENTIAL_UNITS = [
|
|
2471
|
+
"a",
|
|
2472
|
+
"0",
|
|
2473
|
+
" ",
|
|
2474
|
+
"x",
|
|
2475
|
+
"ab",
|
|
2476
|
+
"a.",
|
|
2477
|
+
"a-",
|
|
2478
|
+
"a_",
|
|
2479
|
+
"a@",
|
|
2480
|
+
"a/",
|
|
2481
|
+
"a:",
|
|
2482
|
+
"a=",
|
|
2483
|
+
"a;",
|
|
2484
|
+
"aA0",
|
|
2485
|
+
" "
|
|
2486
|
+
];
|
|
2487
|
+
var EXPONENTIAL_PROBES = EXPONENTIAL_UNITS.flatMap(
|
|
2488
|
+
(unit) => [23, 25].map((len) => unit.repeat(Math.ceil(len / unit.length)).slice(0, len) + "!")
|
|
2489
|
+
);
|
|
2490
|
+
var POLYNOMIAL_PROBES = ["abc-", "a.", "a ", "a=", "x", "0", "a@", "a/", "ab"].map(
|
|
2491
|
+
(unit) => unit.repeat(1e4).slice(0, 4e4) + "!"
|
|
2492
|
+
);
|
|
2493
|
+
|
|
2494
|
+
// ../packages/persistence/src/database.ts
|
|
2495
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
2496
|
+
import { existsSync as existsSync2, renameSync, rmSync } from "fs";
|
|
2497
|
+
import { join as join2, sep } from "path";
|
|
2498
|
+
import { DatabaseSync } from "node:sqlite";
|
|
2499
|
+
|
|
1063
2500
|
// ../packages/schema/src/drizzle/sqlite-ddl.ts
|
|
1064
2501
|
var SQLITE_MIGRATIONS = [
|
|
1065
2502
|
{
|
|
@@ -1105,6 +2542,10 @@ var SQLITE_MIGRATIONS = [
|
|
|
1105
2542
|
{
|
|
1106
2543
|
tag: "0010_events_session_expression_index",
|
|
1107
2544
|
sql: "-- Custom migration: partial expression index for session-scoped finding reads.\n--\n-- sessionFindingsCount, the session-scoped listGroupedFindings paths, and the\n-- insert-time session dedup all filter live-capture events by\n-- json_extract(e.metadata, '$.sessionId') = :sessionId\n-- \u2014 previously a full findings-join scan with a JSON parse per row. The\n-- IS NOT NULL predicate keeps the index to session-stamped events only (an\n-- equality probe implies non-null, so SQLite still uses it).\nCREATE INDEX `idx_events_session_id` ON `events` (json_extract(`metadata`, '$.sessionId')) WHERE json_extract(`metadata`, '$.sessionId') IS NOT NULL;\n"
|
|
2545
|
+
},
|
|
2546
|
+
{
|
|
2547
|
+
tag: "0011_egress_writer",
|
|
2548
|
+
sql: '-- Stable per-project reconcile key for egress call sites, plus a host-keyed\n-- egress decision override that survives destination pruning.\n--\n-- DROP INDEX IF EXISTS, not a bare DROP: the applier replays a pending\n-- migration\'s non-index statements verbatim, so a store that lost\n-- uq_share_call_site out of band would throw here \u2014 and on the plugin hook path\n-- that throw is swallowed fail-open, silently stopping capture.\n--\n-- Existing rows carry the old key\'s discriminator into project_key\n-- (\'legacy:\' || project), so the new unique index is a re-encoding of the old\n-- one and cannot collide on rows the old one allowed. Leaving them on the\n-- column default would collapse two projects\' identical file/line hits onto\n-- one key and abort the whole migration. Writer keys are \'git:\'/\'path:\'-\n-- prefixed, so a backfilled row never collides with a captured one either.\n--\n-- egress_decision_override.destination_id becomes nullable with ON DELETE SET\n-- NULL, which SQLite can only do by rebuilding the table. `host` is ADDed\n-- before the rebuild so the copy has a column to read and so the migration\n-- still presents two probeable columns to the applier\'s evidence check.\nALTER TABLE `share_call_site` ADD `project_key` text DEFAULT \'\' NOT NULL;--> statement-breakpoint\nUPDATE `share_call_site` SET `project_key` = \'legacy:\' || `project`;--> statement-breakpoint\nDROP INDEX IF EXISTS `uq_share_call_site`;--> statement-breakpoint\nCREATE UNIQUE INDEX `uq_share_call_site` ON `share_call_site` (`endpoint_id`,`project_key`,`file`,`line`);--> statement-breakpoint\nCREATE INDEX `idx_share_call_site_project` ON `share_call_site` (`project_key`,`endpoint_id`);--> statement-breakpoint\nALTER TABLE `egress_decision_override` ADD `host` text;--> statement-breakpoint\nPRAGMA foreign_keys=OFF;--> statement-breakpoint\nCREATE TABLE `__new_egress_decision_override` (\n `id` text PRIMARY KEY NOT NULL,\n `destination_id` text,\n `host` text,\n `decision` text NOT NULL,\n `created_at` integer NOT NULL,\n `updated_at` integer NOT NULL,\n FOREIGN KEY (`destination_id`) REFERENCES `share_destination`(`id`) ON UPDATE no action ON DELETE set null\n);\n--> statement-breakpoint\nINSERT INTO `__new_egress_decision_override`("id", "destination_id", "host", "decision", "created_at", "updated_at") SELECT "id", "destination_id", "host", "decision", "created_at", "updated_at" FROM `egress_decision_override`;--> statement-breakpoint\nDROP TABLE `egress_decision_override`;--> statement-breakpoint\nALTER TABLE `__new_egress_decision_override` RENAME TO `egress_decision_override`;--> statement-breakpoint\nPRAGMA foreign_keys=ON;--> statement-breakpoint\nCREATE UNIQUE INDEX `uq_egress_decision_override` ON `egress_decision_override` (`destination_id`);--> statement-breakpoint\nCREATE UNIQUE INDEX `uq_egress_decision_override_host` ON `egress_decision_override` (`host`) WHERE `host` IS NOT NULL;\n'
|
|
1108
2549
|
}
|
|
1109
2550
|
];
|
|
1110
2551
|
|
|
@@ -16883,6 +18324,7 @@ var ExceptionBundleEntry = DetectionException.pick({
|
|
|
16883
18324
|
|
|
16884
18325
|
// ../packages/schema/src/zod/rule.ts
|
|
16885
18326
|
var MatcherType = external_exports.enum(["keyword", "regex", "validator"]).meta({ id: "MatcherType" });
|
|
18327
|
+
var RuleProbeVerdict = external_exports.enum(["safe", "quarantined"]).meta({ id: "RuleProbeVerdict" });
|
|
16886
18328
|
var KeywordMatcher2 = external_exports.object({
|
|
16887
18329
|
type: external_exports.literal("keyword"),
|
|
16888
18330
|
// An empty keyword matches at every position, yielding one zero-length span
|
|
@@ -16907,9 +18349,10 @@ function matchesEmptyString(pattern, flags) {
|
|
|
16907
18349
|
return false;
|
|
16908
18350
|
}
|
|
16909
18351
|
}
|
|
18352
|
+
var MAX_PATTERN_LENGTH = 2e3;
|
|
16910
18353
|
var RegexMatcher2 = external_exports.object({
|
|
16911
18354
|
type: external_exports.literal("regex"),
|
|
16912
|
-
pattern: external_exports.string(),
|
|
18355
|
+
pattern: external_exports.string().min(1).max(MAX_PATTERN_LENGTH),
|
|
16913
18356
|
flags: external_exports.string().default("gi"),
|
|
16914
18357
|
captureGroup: external_exports.number().int().nonnegative().optional()
|
|
16915
18358
|
}).refine((v) => isValidRegex(v.pattern, v.flags), {
|
|
@@ -17396,85 +18839,291 @@ var ImportDetectionRequest = external_exports.object({
|
|
|
17396
18839
|
libraryId: external_exports.string().refine((v) => /^[^/]+\/[^/]+$/.test(v), {
|
|
17397
18840
|
message: "libraryId must be in namespace/packId format"
|
|
17398
18841
|
})
|
|
17399
|
-
}).meta({ id: "ImportDetectionRequest" });
|
|
17400
|
-
|
|
17401
|
-
// ../packages/schema/src/zod/detection-build.ts
|
|
17402
|
-
function summaryToDetectionListItem(s) {
|
|
17403
|
-
return {
|
|
17404
|
-
id: `${s.namespace}/${s.packId}`,
|
|
17405
|
-
name: s.name,
|
|
17406
|
-
version: s.version,
|
|
17407
|
-
enabled: s.enabled,
|
|
17408
|
-
origin: "library",
|
|
17409
|
-
// v1: every installed pack is library origin
|
|
17410
|
-
namespace: s.namespace,
|
|
17411
|
-
packId: s.packId,
|
|
17412
|
-
ruleCount: s.ruleCount,
|
|
17413
|
-
...s.policyId != null ? { policyId: s.policyId } : {},
|
|
17414
|
-
...s.latestVersion != null ? { latestVersion: s.latestVersion } : {}
|
|
17415
|
-
};
|
|
17416
|
-
}
|
|
17417
|
-
function rowToDetectionDetail(row, findingsLast30d, update) {
|
|
17418
|
-
const rules = row.rules.flatMap((r) => {
|
|
17419
|
-
const parsed = Matcher.safeParse(r.matcher);
|
|
17420
|
-
if (!parsed.success) return [];
|
|
17421
|
-
return [
|
|
17422
|
-
{
|
|
17423
|
-
id: r.id,
|
|
17424
|
-
name: r.name,
|
|
17425
|
-
category: r.category,
|
|
17426
|
-
severity: r.severity,
|
|
17427
|
-
matcher: parsed.data
|
|
17428
|
-
}
|
|
17429
|
-
];
|
|
17430
|
-
});
|
|
17431
|
-
return {
|
|
17432
|
-
id: `${row.namespace}/${row.packId}`,
|
|
17433
|
-
name: row.name,
|
|
17434
|
-
version: row.version,
|
|
17435
|
-
enabled: row.enabled,
|
|
17436
|
-
origin: "library",
|
|
17437
|
-
namespace: row.namespace,
|
|
17438
|
-
packId: row.packId,
|
|
17439
|
-
ruleCount: row.rules.length,
|
|
17440
|
-
editedAt: row.updatedAt.toISOString(),
|
|
17441
|
-
findingsLast30d,
|
|
17442
|
-
latestVersion: update ? update.latestVersion : null,
|
|
17443
|
-
update,
|
|
17444
|
-
rules,
|
|
17445
|
-
modified: false,
|
|
17446
|
-
...row.policyId != null ? { policyId: row.policyId } : {}
|
|
17447
|
-
};
|
|
17448
|
-
}
|
|
17449
|
-
function splitDetectionId(id) {
|
|
17450
|
-
const idx = id.indexOf("/");
|
|
17451
|
-
if (idx < 1 || idx === id.length - 1) return null;
|
|
17452
|
-
return { namespace: id.slice(0, idx), packId: id.slice(idx + 1) };
|
|
17453
|
-
}
|
|
17454
|
-
function buildDetectionsList(summaries, query) {
|
|
17455
|
-
const withUpdate = summaries.filter((s) => s.latestVersion != null);
|
|
17456
|
-
const counts = {
|
|
17457
|
-
all: summaries.length,
|
|
17458
|
-
library: summaries.length,
|
|
17459
|
-
// all origin=library in v1
|
|
17460
|
-
custom: 0,
|
|
17461
|
-
customized: 0,
|
|
17462
|
-
updates: withUpdate.length
|
|
17463
|
-
};
|
|
17464
|
-
const filter = query.filter;
|
|
17465
|
-
let filtered = filter === "custom" || filter === "customized" ? [] : filter === "updates" ? [...withUpdate] : [...summaries];
|
|
17466
|
-
if (query.q) {
|
|
17467
|
-
const q = query.q.toLowerCase();
|
|
17468
|
-
filtered = filtered.filter(
|
|
17469
|
-
(s) => s.name.toLowerCase().includes(q) || s.packId.toLowerCase().includes(q) || s.namespace.toLowerCase().includes(q)
|
|
17470
|
-
);
|
|
17471
|
-
}
|
|
17472
|
-
filtered.sort((a, b) => {
|
|
17473
|
-
if (a.enabled !== b.enabled) return a.enabled ? -1 : 1;
|
|
17474
|
-
return a.name.localeCompare(b.name);
|
|
17475
|
-
});
|
|
17476
|
-
return { counts, items: filtered.map(summaryToDetectionListItem) };
|
|
17477
|
-
}
|
|
18842
|
+
}).meta({ id: "ImportDetectionRequest" });
|
|
18843
|
+
|
|
18844
|
+
// ../packages/schema/src/zod/detection-build.ts
|
|
18845
|
+
function summaryToDetectionListItem(s) {
|
|
18846
|
+
return {
|
|
18847
|
+
id: `${s.namespace}/${s.packId}`,
|
|
18848
|
+
name: s.name,
|
|
18849
|
+
version: s.version,
|
|
18850
|
+
enabled: s.enabled,
|
|
18851
|
+
origin: "library",
|
|
18852
|
+
// v1: every installed pack is library origin
|
|
18853
|
+
namespace: s.namespace,
|
|
18854
|
+
packId: s.packId,
|
|
18855
|
+
ruleCount: s.ruleCount,
|
|
18856
|
+
...s.policyId != null ? { policyId: s.policyId } : {},
|
|
18857
|
+
...s.latestVersion != null ? { latestVersion: s.latestVersion } : {}
|
|
18858
|
+
};
|
|
18859
|
+
}
|
|
18860
|
+
function rowToDetectionDetail(row, findingsLast30d, update) {
|
|
18861
|
+
const rules = row.rules.flatMap((r) => {
|
|
18862
|
+
const parsed = Matcher.safeParse(r.matcher);
|
|
18863
|
+
if (!parsed.success) return [];
|
|
18864
|
+
return [
|
|
18865
|
+
{
|
|
18866
|
+
id: r.id,
|
|
18867
|
+
name: r.name,
|
|
18868
|
+
category: r.category,
|
|
18869
|
+
severity: r.severity,
|
|
18870
|
+
matcher: parsed.data
|
|
18871
|
+
}
|
|
18872
|
+
];
|
|
18873
|
+
});
|
|
18874
|
+
return {
|
|
18875
|
+
id: `${row.namespace}/${row.packId}`,
|
|
18876
|
+
name: row.name,
|
|
18877
|
+
version: row.version,
|
|
18878
|
+
enabled: row.enabled,
|
|
18879
|
+
origin: "library",
|
|
18880
|
+
namespace: row.namespace,
|
|
18881
|
+
packId: row.packId,
|
|
18882
|
+
ruleCount: row.rules.length,
|
|
18883
|
+
editedAt: row.updatedAt.toISOString(),
|
|
18884
|
+
findingsLast30d,
|
|
18885
|
+
latestVersion: update ? update.latestVersion : null,
|
|
18886
|
+
update,
|
|
18887
|
+
rules,
|
|
18888
|
+
modified: false,
|
|
18889
|
+
...row.policyId != null ? { policyId: row.policyId } : {}
|
|
18890
|
+
};
|
|
18891
|
+
}
|
|
18892
|
+
function splitDetectionId(id) {
|
|
18893
|
+
const idx = id.indexOf("/");
|
|
18894
|
+
if (idx < 1 || idx === id.length - 1) return null;
|
|
18895
|
+
return { namespace: id.slice(0, idx), packId: id.slice(idx + 1) };
|
|
18896
|
+
}
|
|
18897
|
+
function buildDetectionsList(summaries, query) {
|
|
18898
|
+
const withUpdate = summaries.filter((s) => s.latestVersion != null);
|
|
18899
|
+
const counts = {
|
|
18900
|
+
all: summaries.length,
|
|
18901
|
+
library: summaries.length,
|
|
18902
|
+
// all origin=library in v1
|
|
18903
|
+
custom: 0,
|
|
18904
|
+
customized: 0,
|
|
18905
|
+
updates: withUpdate.length
|
|
18906
|
+
};
|
|
18907
|
+
const filter = query.filter;
|
|
18908
|
+
let filtered = filter === "custom" || filter === "customized" ? [] : filter === "updates" ? [...withUpdate] : [...summaries];
|
|
18909
|
+
if (query.q) {
|
|
18910
|
+
const q = query.q.toLowerCase();
|
|
18911
|
+
filtered = filtered.filter(
|
|
18912
|
+
(s) => s.name.toLowerCase().includes(q) || s.packId.toLowerCase().includes(q) || s.namespace.toLowerCase().includes(q)
|
|
18913
|
+
);
|
|
18914
|
+
}
|
|
18915
|
+
filtered.sort((a, b) => {
|
|
18916
|
+
if (a.enabled !== b.enabled) return a.enabled ? -1 : 1;
|
|
18917
|
+
return a.name.localeCompare(b.name);
|
|
18918
|
+
});
|
|
18919
|
+
return { counts, items: filtered.map(summaryToDetectionListItem) };
|
|
18920
|
+
}
|
|
18921
|
+
|
|
18922
|
+
// ../packages/schema/src/zod/shares.ts
|
|
18923
|
+
var DestinationKind = external_exports.enum(["provider", "internal", "external", "ip"]).meta({ id: "DestinationKind" });
|
|
18924
|
+
var Transport = external_exports.enum(["https", "http", "sftp", "grpc", "smtp", "ws", "wss"]).meta({ id: "Transport" });
|
|
18925
|
+
var DataClass = external_exports.enum(["secrets", "pii", "customer", "source", "telemetry", "logs", "metrics", "none"]).meta({ id: "DataClass" });
|
|
18926
|
+
var DATA_CLASS_ORDER = DataClass.options;
|
|
18927
|
+
var ShareTrustLevel = external_exports.enum(["recognized", "internal", "unverified", "ip"]).meta({ id: "ShareTrustLevel" });
|
|
18928
|
+
var EgressDecision = external_exports.enum(["allow", "block"]).meta({ id: "EgressDecision" });
|
|
18929
|
+
var EgressStatus = external_exports.enum(["allowed", "blocked", "review"]).meta({ id: "EgressStatus" });
|
|
18930
|
+
var ReviewReason = external_exports.enum(["raw_ip", "unverified_domain", "plaintext_transport"]).meta({ id: "ReviewReason" });
|
|
18931
|
+
var HttpMethod = external_exports.enum(["GET", "POST", "PUT", "DELETE", "SDK", "REF"]).meta({ id: "HttpMethod" });
|
|
18932
|
+
var ReviewInfo = external_exports.object({
|
|
18933
|
+
needsReview: external_exports.boolean(),
|
|
18934
|
+
reasons: external_exports.array(ReviewReason)
|
|
18935
|
+
}).meta({ id: "ReviewInfo" });
|
|
18936
|
+
var DestinationNetwork = external_exports.object({
|
|
18937
|
+
port: external_exports.number().int().nullable(),
|
|
18938
|
+
geo: external_exports.string().nullable(),
|
|
18939
|
+
ptr: external_exports.string().nullable()
|
|
18940
|
+
}).meta({ id: "DestinationNetwork" });
|
|
18941
|
+
var EndpointSummary = external_exports.object({
|
|
18942
|
+
id: external_exports.string(),
|
|
18943
|
+
method: HttpMethod,
|
|
18944
|
+
transport: Transport,
|
|
18945
|
+
url: external_exports.string(),
|
|
18946
|
+
template: external_exports.boolean(),
|
|
18947
|
+
dataClass: DataClass,
|
|
18948
|
+
lastSeen: external_exports.iso.datetime(),
|
|
18949
|
+
callSiteCount: external_exports.number().int().nonnegative()
|
|
18950
|
+
}).meta({ id: "EndpointSummary" });
|
|
18951
|
+
var CallSite = external_exports.object({
|
|
18952
|
+
id: external_exports.string(),
|
|
18953
|
+
project: external_exports.string(),
|
|
18954
|
+
file: external_exports.string(),
|
|
18955
|
+
line: external_exports.number().int().nonnegative(),
|
|
18956
|
+
snippet: external_exports.string(),
|
|
18957
|
+
dynamic: external_exports.boolean(),
|
|
18958
|
+
vendored: external_exports.boolean(),
|
|
18959
|
+
/** Deep-link to the Inventory project, when the repo is governed there. */
|
|
18960
|
+
projectId: external_exports.string().nullable()
|
|
18961
|
+
}).meta({ id: "CallSite" });
|
|
18962
|
+
var EndpointWithSites = EndpointSummary.extend({
|
|
18963
|
+
sites: external_exports.array(CallSite)
|
|
18964
|
+
}).meta({ id: "EndpointWithSites" });
|
|
18965
|
+
var ShareDestinationSummary = external_exports.object({
|
|
18966
|
+
id: external_exports.string(),
|
|
18967
|
+
kind: DestinationKind,
|
|
18968
|
+
name: external_exports.string(),
|
|
18969
|
+
host: external_exports.string(),
|
|
18970
|
+
category: external_exports.string(),
|
|
18971
|
+
trust: ShareTrustLevel,
|
|
18972
|
+
/** Effective state (decision applied over the trust default). */
|
|
18973
|
+
status: EgressStatus,
|
|
18974
|
+
/** True when an egress decision override differs from the trust default. */
|
|
18975
|
+
isCustom: external_exports.boolean(),
|
|
18976
|
+
lastSeen: external_exports.iso.datetime(),
|
|
18977
|
+
endpointCount: external_exports.number().int().nonnegative(),
|
|
18978
|
+
callSiteCount: external_exports.number().int().nonnegative(),
|
|
18979
|
+
transports: external_exports.array(Transport),
|
|
18980
|
+
/** Most-sensitive first. */
|
|
18981
|
+
dataClasses: external_exports.array(DataClass),
|
|
18982
|
+
review: ReviewInfo,
|
|
18983
|
+
/** Non-provider hosts only; null for providers. */
|
|
18984
|
+
network: DestinationNetwork.nullable(),
|
|
18985
|
+
/** Embedded for inline expansion — no call sites here. */
|
|
18986
|
+
endpoints: external_exports.array(EndpointSummary)
|
|
18987
|
+
}).meta({ id: "ShareDestinationSummary" });
|
|
18988
|
+
var ShareDestinationDetail = ShareDestinationSummary.omit({
|
|
18989
|
+
endpointCount: true,
|
|
18990
|
+
callSiteCount: true,
|
|
18991
|
+
endpoints: true
|
|
18992
|
+
}).extend({
|
|
18993
|
+
/** Ownership/geo rationale; null for providers. */
|
|
18994
|
+
note: external_exports.string().nullable(),
|
|
18995
|
+
endpoints: external_exports.array(EndpointWithSites)
|
|
18996
|
+
}).meta({ id: "ShareDestinationDetail" });
|
|
18997
|
+
var ReviewDestination = external_exports.object({
|
|
18998
|
+
id: external_exports.string(),
|
|
18999
|
+
kind: DestinationKind,
|
|
19000
|
+
name: external_exports.string(),
|
|
19001
|
+
/** Registrable host — lets the strip derive the provider lettermark, as the register does. */
|
|
19002
|
+
host: external_exports.string(),
|
|
19003
|
+
trust: ShareTrustLevel,
|
|
19004
|
+
status: EgressStatus,
|
|
19005
|
+
review: ReviewInfo,
|
|
19006
|
+
topDataClass: DataClass,
|
|
19007
|
+
callSiteCount: external_exports.number().int().nonnegative(),
|
|
19008
|
+
lastSeen: external_exports.iso.datetime()
|
|
19009
|
+
}).meta({ id: "ReviewDestination" });
|
|
19010
|
+
var ShareDestinationGroup = external_exports.object({
|
|
19011
|
+
kind: DestinationKind,
|
|
19012
|
+
total: external_exports.number().int().nonnegative(),
|
|
19013
|
+
items: external_exports.array(ShareDestinationSummary)
|
|
19014
|
+
}).meta({ id: "ShareDestinationGroup" });
|
|
19015
|
+
var ListShareDestinationsResponse = external_exports.object({ groups: external_exports.array(ShareDestinationGroup) }).meta({ id: "ListShareDestinationsResponse" });
|
|
19016
|
+
var NeedsReviewResponse = external_exports.object({ items: external_exports.array(ReviewDestination) }).meta({ id: "NeedsReviewResponse" });
|
|
19017
|
+
var SharesStats = external_exports.object({
|
|
19018
|
+
destinations: external_exports.number().int().nonnegative(),
|
|
19019
|
+
endpoints: external_exports.number().int().nonnegative(),
|
|
19020
|
+
callSites: external_exports.number().int().nonnegative(),
|
|
19021
|
+
needsReview: external_exports.number().int().nonnegative(),
|
|
19022
|
+
insecure: external_exports.number().int().nonnegative(),
|
|
19023
|
+
byKind: external_exports.object({
|
|
19024
|
+
provider: external_exports.number().int().nonnegative(),
|
|
19025
|
+
internal: external_exports.number().int().nonnegative(),
|
|
19026
|
+
external: external_exports.number().int().nonnegative(),
|
|
19027
|
+
ip: external_exports.number().int().nonnegative()
|
|
19028
|
+
}),
|
|
19029
|
+
byTrust: external_exports.object({
|
|
19030
|
+
recognized: external_exports.number().int().nonnegative(),
|
|
19031
|
+
internal: external_exports.number().int().nonnegative(),
|
|
19032
|
+
unverified: external_exports.number().int().nonnegative(),
|
|
19033
|
+
ip: external_exports.number().int().nonnegative()
|
|
19034
|
+
})
|
|
19035
|
+
}).meta({ id: "SharesStats" });
|
|
19036
|
+
var SetEgressDecisionBody = external_exports.object({
|
|
19037
|
+
/** `null` clears the override — reverts to the trust default, isCustom false. */
|
|
19038
|
+
decision: EgressDecision.nullable()
|
|
19039
|
+
}).meta({ id: "SetEgressDecisionBody" });
|
|
19040
|
+
var SetEgressDecisionResponse = external_exports.object({ destination: ShareDestinationSummary }).meta({ id: "SetEgressDecisionResponse" });
|
|
19041
|
+
var ListShareDestinationsQuery = external_exports.object({
|
|
19042
|
+
/** Case-insensitive match over destination name/category, endpoint url, call-site project/file. */
|
|
19043
|
+
q: external_exports.string().optional(),
|
|
19044
|
+
/** Repeatable. Restrict to these DestinationKind values; absent means all kinds. */
|
|
19045
|
+
kind: external_exports.array(DestinationKind).optional(),
|
|
19046
|
+
/** Reserved for future grouping modes; only 'destination' is supported today. */
|
|
19047
|
+
groupBy: external_exports.enum(["destination"]).default("destination"),
|
|
19048
|
+
/**
|
|
19049
|
+
* When true, return a flat severity-ordered `items[]` instead of `groups`.
|
|
19050
|
+
* Uses `z.stringbool()` (NOT `z.coerce.boolean()` — `Boolean(str)` is true for
|
|
19051
|
+
* any non-empty string, so `?review=false`/`?review=0` would wrongly coerce
|
|
19052
|
+
* to `true`). `z.stringbool()` parses true/1/yes vs false/0/no correctly.
|
|
19053
|
+
*/
|
|
19054
|
+
review: external_exports.stringbool().default(false)
|
|
19055
|
+
});
|
|
19056
|
+
var ExportSharesQuery = external_exports.object({
|
|
19057
|
+
format: external_exports.enum(["csv", "json"]).default("csv"),
|
|
19058
|
+
q: external_exports.string().optional(),
|
|
19059
|
+
kind: external_exports.array(DestinationKind).optional()
|
|
19060
|
+
});
|
|
19061
|
+
|
|
19062
|
+
// ../packages/schema/src/zod/egress-extraction.ts
|
|
19063
|
+
var EgressEcosystem = external_exports.enum(["npm", "pypi", "go", "maven", "rubygems", "cargo", "composer", "nuget"]).meta({ id: "EgressEcosystem" });
|
|
19064
|
+
var ProviderRegistryEntry = external_exports.object({
|
|
19065
|
+
id: external_exports.string(),
|
|
19066
|
+
name: external_exports.string(),
|
|
19067
|
+
category: external_exports.string(),
|
|
19068
|
+
/** Suffix-matched: 'stripe.com' matches api.stripe.com, never evilstripe.com. */
|
|
19069
|
+
hostSuffixes: external_exports.array(external_exports.string()).min(1),
|
|
19070
|
+
/** Canonical API base URL recorded for manifest-derived (method 'SDK') endpoints. */
|
|
19071
|
+
apiBase: external_exports.string(),
|
|
19072
|
+
/** Most-sensitive first; index 0 becomes the endpoint dataClass. */
|
|
19073
|
+
defaultDataClasses: external_exports.array(DataClass).min(1),
|
|
19074
|
+
/** SDK identifiers per ecosystem ('go' prefix-matched by path, 'maven' by group-id prefix). */
|
|
19075
|
+
sdks: external_exports.partialRecord(EgressEcosystem, external_exports.array(external_exports.string()))
|
|
19076
|
+
}).meta({ id: "ProviderRegistryEntry" });
|
|
19077
|
+
var EgressCallSiteHit = external_exports.object({
|
|
19078
|
+
file: external_exports.string(),
|
|
19079
|
+
line: external_exports.number().int().positive(),
|
|
19080
|
+
snippet: external_exports.string(),
|
|
19081
|
+
dynamic: external_exports.boolean(),
|
|
19082
|
+
vendored: external_exports.boolean()
|
|
19083
|
+
}).meta({ id: "EgressCallSiteHit" });
|
|
19084
|
+
var ResolvedEgressHit = external_exports.object({
|
|
19085
|
+
host: external_exports.string(),
|
|
19086
|
+
kind: DestinationKind,
|
|
19087
|
+
name: external_exports.string(),
|
|
19088
|
+
category: external_exports.string(),
|
|
19089
|
+
trust: ShareTrustLevel,
|
|
19090
|
+
network: DestinationNetwork.nullable(),
|
|
19091
|
+
method: HttpMethod,
|
|
19092
|
+
transport: Transport,
|
|
19093
|
+
url: external_exports.string(),
|
|
19094
|
+
template: external_exports.boolean(),
|
|
19095
|
+
dataClass: DataClass,
|
|
19096
|
+
site: EgressCallSiteHit
|
|
19097
|
+
}).meta({ id: "ResolvedEgressHit" });
|
|
19098
|
+
var EgressReconcile = external_exports.discriminatedUnion("mode", [
|
|
19099
|
+
external_exports.object({ mode: external_exports.literal("walk"), walkedPrefix: external_exports.string() }),
|
|
19100
|
+
external_exports.object({
|
|
19101
|
+
mode: external_exports.literal("ledger"),
|
|
19102
|
+
scannedFiles: external_exports.array(external_exports.string()),
|
|
19103
|
+
deletedFiles: external_exports.array(external_exports.string())
|
|
19104
|
+
})
|
|
19105
|
+
]).meta({ id: "EgressReconcile" });
|
|
19106
|
+
var RecordProjectEgressInput = external_exports.object({
|
|
19107
|
+
/** Stable reconcile key: 'git:<repo identity>' or 'path:<abs root>' (non-git). */
|
|
19108
|
+
projectKey: external_exports.string().min(1),
|
|
19109
|
+
/** Display name only — never keys reconciliation. */
|
|
19110
|
+
project: external_exports.string(),
|
|
19111
|
+
projectId: external_exports.string().nullable(),
|
|
19112
|
+
reconcile: EgressReconcile,
|
|
19113
|
+
hits: external_exports.array(ResolvedEgressHit)
|
|
19114
|
+
}).meta({ id: "RecordProjectEgressInput" });
|
|
19115
|
+
var EgressWriteSummary = external_exports.object({
|
|
19116
|
+
destinations: external_exports.number().int().nonnegative(),
|
|
19117
|
+
endpoints: external_exports.number().int().nonnegative(),
|
|
19118
|
+
callSites: external_exports.number().int().nonnegative(),
|
|
19119
|
+
truncated: external_exports.boolean(),
|
|
19120
|
+
/**
|
|
19121
|
+
* Files the cap dropped whole. Their stored rows were left untouched, so a
|
|
19122
|
+
* ledger-keeping caller must withhold their ledger entries and read them
|
|
19123
|
+
* again next scan.
|
|
19124
|
+
*/
|
|
19125
|
+
droppedFiles: external_exports.array(external_exports.string()).default([])
|
|
19126
|
+
}).meta({ id: "EgressWriteSummary" });
|
|
17478
19127
|
|
|
17479
19128
|
// ../packages/schema/src/zod/findings-group-build.ts
|
|
17480
19129
|
function toApiAction(dbVal) {
|
|
@@ -17737,7 +19386,7 @@ var PatchInstalledPackRequest = external_exports.object({
|
|
|
17737
19386
|
}).meta({ id: "PatchInstalledPackRequest" });
|
|
17738
19387
|
|
|
17739
19388
|
// ../packages/schema/src/zod/local.ts
|
|
17740
|
-
var WORKSPACE_SETTINGS_SPEC_VERSION =
|
|
19389
|
+
var WORKSPACE_SETTINGS_SPEC_VERSION = 3;
|
|
17741
19390
|
var RunMode = external_exports.enum(["standalone"]);
|
|
17742
19391
|
var SimpleDetectionPolicy = external_exports.enum(["redact", "warn"]);
|
|
17743
19392
|
var HistoricalAccess = external_exports.enum(["full", "session-only"]);
|
|
@@ -17752,6 +19401,9 @@ var WorkspaceSettings = external_exports.object({
|
|
|
17752
19401
|
policy: SimpleDetectionPolicy.default("redact"),
|
|
17753
19402
|
// Consent for scanning pre-install surfaces; opt-in (see HistoricalAccess).
|
|
17754
19403
|
historicalAccess: HistoricalAccess.default("session-only"),
|
|
19404
|
+
// In-place egress extraction on the scan paths; disable to stop all Data
|
|
19405
|
+
// Shares writes.
|
|
19406
|
+
dataSharesInPlace: external_exports.boolean().default(true),
|
|
17755
19407
|
// Absent until /aka:setup completes; its presence is what "onboarded" means.
|
|
17756
19408
|
onboardedAt: external_exports.iso.datetime().optional()
|
|
17757
19409
|
});
|
|
@@ -18161,220 +19813,81 @@ var TriageRecommendation = external_exports.object({
|
|
|
18161
19813
|
});
|
|
18162
19814
|
|
|
18163
19815
|
// ../packages/schema/src/zod/setup-frame.ts
|
|
18164
|
-
var CalibrationCounts = external_exports.object({
|
|
18165
|
-
total: external_exports.number().int().nonnegative(),
|
|
18166
|
-
important: external_exports.number().int().nonnegative(),
|
|
18167
|
-
routine: external_exports.number().int().nonnegative()
|
|
18168
|
-
}).refine((c) => c.total === c.important + c.routine, {
|
|
18169
|
-
message: "total must equal important + routine",
|
|
18170
|
-
path: ["total"]
|
|
18171
|
-
});
|
|
18172
|
-
var FalsePositivePatternValue = external_exports.object({
|
|
18173
|
-
ruleId: external_exports.string(),
|
|
18174
|
-
category: DetectionCategory,
|
|
18175
|
-
valueFingerprint: external_exports.string(),
|
|
18176
|
-
keyVersion: external_exports.number().int().nonnegative()
|
|
18177
|
-
});
|
|
18178
|
-
var FalsePositivePatternGroup = external_exports.object({
|
|
18179
|
-
pattern: external_exports.string(),
|
|
18180
|
-
count: external_exports.number().int().nonnegative(),
|
|
18181
|
-
values: external_exports.array(FalsePositivePatternValue).min(1)
|
|
18182
|
-
});
|
|
18183
|
-
var CalibrationFindingKind = external_exports.object({
|
|
18184
|
-
category: DetectionCategory,
|
|
18185
|
-
count: external_exports.number().int().nonnegative(),
|
|
18186
|
-
egress: external_exports.boolean()
|
|
18187
|
-
});
|
|
18188
|
-
var CalibrationFrame = external_exports.object({
|
|
18189
|
-
counts: CalibrationCounts,
|
|
18190
|
-
routineCategories: external_exports.array(DetectionCategory),
|
|
18191
|
-
surfacedCategories: external_exports.array(DetectionCategory),
|
|
18192
|
-
findingKinds: external_exports.array(CalibrationFindingKind),
|
|
18193
|
-
posture: external_exports.record(DetectionCategory, BuiltinPolicyId),
|
|
18194
|
-
maskedFindings: external_exports.array(MaskedSecretFinding).optional(),
|
|
18195
|
-
falsePositivePatterns: external_exports.array(FalsePositivePatternGroup).optional()
|
|
18196
|
-
});
|
|
18197
|
-
var CalibrationPreviewCategory = TriageCategoryRec.pick({
|
|
18198
|
-
category: true,
|
|
18199
|
-
genuineCount: true,
|
|
18200
|
-
fpCount: true
|
|
18201
|
-
}).extend({
|
|
18202
|
-
egress: external_exports.boolean()
|
|
18203
|
-
});
|
|
18204
|
-
var CalibrationPreview = external_exports.object({
|
|
18205
|
-
categories: external_exports.array(CalibrationPreviewCategory),
|
|
18206
|
-
posture: external_exports.record(DetectionCategory, BuiltinPolicyId)
|
|
18207
|
-
});
|
|
18208
|
-
var CalibrationResult = external_exports.object({
|
|
18209
|
-
frame: CalibrationFrame,
|
|
18210
|
-
copy: external_exports.string()
|
|
18211
|
-
});
|
|
18212
|
-
var FirstRunCalibration = external_exports.enum(["scan", "floor"]);
|
|
18213
|
-
var SetupHandoffOption = external_exports.object({
|
|
18214
|
-
id: external_exports.enum(["enter-remediation", "open-dashboard", "not-now"]),
|
|
18215
|
-
label: external_exports.string()
|
|
18216
|
-
});
|
|
18217
|
-
var DashboardHandoffOptions = external_exports.tuple([
|
|
18218
|
-
SetupHandoffOption.extend({ id: external_exports.literal("open-dashboard") }),
|
|
18219
|
-
SetupHandoffOption.extend({ id: external_exports.literal("not-now") })
|
|
18220
|
-
]);
|
|
18221
|
-
var ComposedRemediationOptions = external_exports.tuple([
|
|
18222
|
-
SetupHandoffOption.extend({ id: external_exports.literal("enter-remediation") }),
|
|
18223
|
-
SetupHandoffOption.extend({ id: external_exports.literal("open-dashboard") }),
|
|
18224
|
-
SetupHandoffOption.extend({ id: external_exports.literal("not-now") })
|
|
18225
|
-
]);
|
|
18226
|
-
var SetupHandoffOffer = external_exports.object({
|
|
18227
|
-
worthALook: external_exports.number().int().nonnegative(),
|
|
18228
|
-
liveKeys: external_exports.number().int().nonnegative().optional(),
|
|
18229
|
-
options: external_exports.union([DashboardHandoffOptions, ComposedRemediationOptions])
|
|
18230
|
-
}).refine(
|
|
18231
|
-
(o) => o.options.some((opt) => opt.id === "enter-remediation") === (o.liveKeys ?? 0) > 0,
|
|
18232
|
-
{
|
|
18233
|
-
message: "the chain-entry option is present exactly when liveKeys > 0",
|
|
18234
|
-
path: ["options"]
|
|
18235
|
-
}
|
|
18236
|
-
).refine((o) => (o.liveKeys ?? 0) <= o.worthALook, {
|
|
18237
|
-
message: "liveKeys is a subset of worthALook and cannot exceed it",
|
|
18238
|
-
path: ["liveKeys"]
|
|
18239
|
-
});
|
|
18240
|
-
|
|
18241
|
-
// ../packages/schema/src/zod/shares.ts
|
|
18242
|
-
var DestinationKind = external_exports.enum(["provider", "internal", "ip"]).meta({ id: "DestinationKind" });
|
|
18243
|
-
var Transport = external_exports.enum(["https", "http", "sftp", "grpc", "smtp"]).meta({ id: "Transport" });
|
|
18244
|
-
var DataClass = external_exports.enum(["secrets", "pii", "customer", "source", "telemetry", "logs", "metrics", "none"]).meta({ id: "DataClass" });
|
|
18245
|
-
var DATA_CLASS_ORDER = DataClass.options;
|
|
18246
|
-
var ShareTrustLevel = external_exports.enum(["recognized", "internal", "unverified", "ip"]).meta({ id: "ShareTrustLevel" });
|
|
18247
|
-
var EgressDecision = external_exports.enum(["allow", "block"]).meta({ id: "EgressDecision" });
|
|
18248
|
-
var EgressStatus = external_exports.enum(["allowed", "blocked", "review"]).meta({ id: "EgressStatus" });
|
|
18249
|
-
var ReviewReason = external_exports.enum(["raw_ip", "unverified_domain", "plaintext_transport"]).meta({ id: "ReviewReason" });
|
|
18250
|
-
var HttpMethod = external_exports.enum(["GET", "POST", "PUT", "DELETE"]).meta({ id: "HttpMethod" });
|
|
18251
|
-
var ReviewInfo = external_exports.object({
|
|
18252
|
-
needsReview: external_exports.boolean(),
|
|
18253
|
-
reasons: external_exports.array(ReviewReason)
|
|
18254
|
-
}).meta({ id: "ReviewInfo" });
|
|
18255
|
-
var DestinationNetwork = external_exports.object({
|
|
18256
|
-
port: external_exports.number().int().nullable(),
|
|
18257
|
-
geo: external_exports.string().nullable(),
|
|
18258
|
-
ptr: external_exports.string().nullable()
|
|
18259
|
-
}).meta({ id: "DestinationNetwork" });
|
|
18260
|
-
var EndpointSummary = external_exports.object({
|
|
18261
|
-
id: external_exports.string(),
|
|
18262
|
-
method: HttpMethod,
|
|
18263
|
-
transport: Transport,
|
|
18264
|
-
url: external_exports.string(),
|
|
18265
|
-
template: external_exports.boolean(),
|
|
18266
|
-
dataClass: DataClass,
|
|
18267
|
-
lastSeen: external_exports.iso.datetime(),
|
|
18268
|
-
callSiteCount: external_exports.number().int().nonnegative()
|
|
18269
|
-
}).meta({ id: "EndpointSummary" });
|
|
18270
|
-
var CallSite = external_exports.object({
|
|
18271
|
-
id: external_exports.string(),
|
|
18272
|
-
project: external_exports.string(),
|
|
18273
|
-
file: external_exports.string(),
|
|
18274
|
-
line: external_exports.number().int().nonnegative(),
|
|
18275
|
-
snippet: external_exports.string(),
|
|
18276
|
-
dynamic: external_exports.boolean(),
|
|
18277
|
-
vendored: external_exports.boolean(),
|
|
18278
|
-
/** Deep-link to the Inventory project, when the repo is governed there. */
|
|
18279
|
-
projectId: external_exports.string().nullable()
|
|
18280
|
-
}).meta({ id: "CallSite" });
|
|
18281
|
-
var EndpointWithSites = EndpointSummary.extend({
|
|
18282
|
-
sites: external_exports.array(CallSite)
|
|
18283
|
-
}).meta({ id: "EndpointWithSites" });
|
|
18284
|
-
var ShareDestinationSummary = external_exports.object({
|
|
18285
|
-
id: external_exports.string(),
|
|
18286
|
-
kind: DestinationKind,
|
|
18287
|
-
name: external_exports.string(),
|
|
18288
|
-
host: external_exports.string(),
|
|
18289
|
-
category: external_exports.string(),
|
|
18290
|
-
trust: ShareTrustLevel,
|
|
18291
|
-
/** Effective state (decision applied over the trust default). */
|
|
18292
|
-
status: EgressStatus,
|
|
18293
|
-
/** True when an egress decision override differs from the trust default. */
|
|
18294
|
-
isCustom: external_exports.boolean(),
|
|
18295
|
-
lastSeen: external_exports.iso.datetime(),
|
|
18296
|
-
endpointCount: external_exports.number().int().nonnegative(),
|
|
18297
|
-
callSiteCount: external_exports.number().int().nonnegative(),
|
|
18298
|
-
transports: external_exports.array(Transport),
|
|
18299
|
-
/** Most-sensitive first. */
|
|
18300
|
-
dataClasses: external_exports.array(DataClass),
|
|
18301
|
-
review: ReviewInfo,
|
|
18302
|
-
/** Non-provider hosts only; null for providers. */
|
|
18303
|
-
network: DestinationNetwork.nullable(),
|
|
18304
|
-
/** Embedded for inline expansion — no call sites here. */
|
|
18305
|
-
endpoints: external_exports.array(EndpointSummary)
|
|
18306
|
-
}).meta({ id: "ShareDestinationSummary" });
|
|
18307
|
-
var ShareDestinationDetail = ShareDestinationSummary.omit({
|
|
18308
|
-
endpointCount: true,
|
|
18309
|
-
callSiteCount: true,
|
|
18310
|
-
endpoints: true
|
|
18311
|
-
}).extend({
|
|
18312
|
-
/** Ownership/geo rationale; null for providers. */
|
|
18313
|
-
note: external_exports.string().nullable(),
|
|
18314
|
-
endpoints: external_exports.array(EndpointWithSites)
|
|
18315
|
-
}).meta({ id: "ShareDestinationDetail" });
|
|
18316
|
-
var ReviewDestination = external_exports.object({
|
|
18317
|
-
id: external_exports.string(),
|
|
18318
|
-
kind: DestinationKind,
|
|
18319
|
-
name: external_exports.string(),
|
|
18320
|
-
/** Registrable host — lets the strip derive the provider lettermark, as the register does. */
|
|
18321
|
-
host: external_exports.string(),
|
|
18322
|
-
trust: ShareTrustLevel,
|
|
18323
|
-
status: EgressStatus,
|
|
18324
|
-
review: ReviewInfo,
|
|
18325
|
-
topDataClass: DataClass,
|
|
18326
|
-
callSiteCount: external_exports.number().int().nonnegative(),
|
|
18327
|
-
lastSeen: external_exports.iso.datetime()
|
|
18328
|
-
}).meta({ id: "ReviewDestination" });
|
|
18329
|
-
var ShareDestinationGroup = external_exports.object({
|
|
18330
|
-
kind: DestinationKind,
|
|
19816
|
+
var CalibrationCounts = external_exports.object({
|
|
18331
19817
|
total: external_exports.number().int().nonnegative(),
|
|
18332
|
-
|
|
18333
|
-
|
|
18334
|
-
|
|
18335
|
-
|
|
18336
|
-
|
|
18337
|
-
destinations: external_exports.number().int().nonnegative(),
|
|
18338
|
-
endpoints: external_exports.number().int().nonnegative(),
|
|
18339
|
-
callSites: external_exports.number().int().nonnegative(),
|
|
18340
|
-
needsReview: external_exports.number().int().nonnegative(),
|
|
18341
|
-
insecure: external_exports.number().int().nonnegative(),
|
|
18342
|
-
byKind: external_exports.object({
|
|
18343
|
-
provider: external_exports.number().int().nonnegative(),
|
|
18344
|
-
internal: external_exports.number().int().nonnegative(),
|
|
18345
|
-
ip: external_exports.number().int().nonnegative()
|
|
18346
|
-
}),
|
|
18347
|
-
byTrust: external_exports.object({
|
|
18348
|
-
recognized: external_exports.number().int().nonnegative(),
|
|
18349
|
-
internal: external_exports.number().int().nonnegative(),
|
|
18350
|
-
unverified: external_exports.number().int().nonnegative(),
|
|
18351
|
-
ip: external_exports.number().int().nonnegative()
|
|
18352
|
-
})
|
|
18353
|
-
}).meta({ id: "SharesStats" });
|
|
18354
|
-
var SetEgressDecisionBody = external_exports.object({
|
|
18355
|
-
/** `null` clears the override — reverts to the trust default, isCustom false. */
|
|
18356
|
-
decision: EgressDecision.nullable()
|
|
18357
|
-
}).meta({ id: "SetEgressDecisionBody" });
|
|
18358
|
-
var SetEgressDecisionResponse = external_exports.object({ destination: ShareDestinationSummary }).meta({ id: "SetEgressDecisionResponse" });
|
|
18359
|
-
var ListShareDestinationsQuery = external_exports.object({
|
|
18360
|
-
/** Case-insensitive match over destination name/category, endpoint url, call-site project/file. */
|
|
18361
|
-
q: external_exports.string().optional(),
|
|
18362
|
-
/** Repeatable. Restrict to these DestinationKind values; absent means all kinds. */
|
|
18363
|
-
kind: external_exports.array(DestinationKind).optional(),
|
|
18364
|
-
/** Reserved for future grouping modes; only 'destination' is supported today. */
|
|
18365
|
-
groupBy: external_exports.enum(["destination"]).default("destination"),
|
|
18366
|
-
/**
|
|
18367
|
-
* When true, return a flat severity-ordered `items[]` instead of `groups`.
|
|
18368
|
-
* Uses `z.stringbool()` (NOT `z.coerce.boolean()` — `Boolean(str)` is true for
|
|
18369
|
-
* any non-empty string, so `?review=false`/`?review=0` would wrongly coerce
|
|
18370
|
-
* to `true`). `z.stringbool()` parses true/1/yes vs false/0/no correctly.
|
|
18371
|
-
*/
|
|
18372
|
-
review: external_exports.stringbool().default(false)
|
|
19818
|
+
important: external_exports.number().int().nonnegative(),
|
|
19819
|
+
routine: external_exports.number().int().nonnegative()
|
|
19820
|
+
}).refine((c) => c.total === c.important + c.routine, {
|
|
19821
|
+
message: "total must equal important + routine",
|
|
19822
|
+
path: ["total"]
|
|
18373
19823
|
});
|
|
18374
|
-
var
|
|
18375
|
-
|
|
18376
|
-
|
|
18377
|
-
|
|
19824
|
+
var FalsePositivePatternValue = external_exports.object({
|
|
19825
|
+
ruleId: external_exports.string(),
|
|
19826
|
+
category: DetectionCategory,
|
|
19827
|
+
valueFingerprint: external_exports.string(),
|
|
19828
|
+
keyVersion: external_exports.number().int().nonnegative()
|
|
19829
|
+
});
|
|
19830
|
+
var FalsePositivePatternGroup = external_exports.object({
|
|
19831
|
+
pattern: external_exports.string(),
|
|
19832
|
+
count: external_exports.number().int().nonnegative(),
|
|
19833
|
+
values: external_exports.array(FalsePositivePatternValue).min(1)
|
|
19834
|
+
});
|
|
19835
|
+
var CalibrationFindingKind = external_exports.object({
|
|
19836
|
+
category: DetectionCategory,
|
|
19837
|
+
count: external_exports.number().int().nonnegative(),
|
|
19838
|
+
egress: external_exports.boolean()
|
|
19839
|
+
});
|
|
19840
|
+
var CalibrationFrame = external_exports.object({
|
|
19841
|
+
counts: CalibrationCounts,
|
|
19842
|
+
routineCategories: external_exports.array(DetectionCategory),
|
|
19843
|
+
surfacedCategories: external_exports.array(DetectionCategory),
|
|
19844
|
+
findingKinds: external_exports.array(CalibrationFindingKind),
|
|
19845
|
+
posture: external_exports.record(DetectionCategory, BuiltinPolicyId),
|
|
19846
|
+
maskedFindings: external_exports.array(MaskedSecretFinding).optional(),
|
|
19847
|
+
falsePositivePatterns: external_exports.array(FalsePositivePatternGroup).optional()
|
|
19848
|
+
});
|
|
19849
|
+
var CalibrationPreviewCategory = TriageCategoryRec.pick({
|
|
19850
|
+
category: true,
|
|
19851
|
+
genuineCount: true,
|
|
19852
|
+
fpCount: true
|
|
19853
|
+
}).extend({
|
|
19854
|
+
egress: external_exports.boolean()
|
|
19855
|
+
});
|
|
19856
|
+
var CalibrationPreview = external_exports.object({
|
|
19857
|
+
categories: external_exports.array(CalibrationPreviewCategory),
|
|
19858
|
+
posture: external_exports.record(DetectionCategory, BuiltinPolicyId)
|
|
19859
|
+
});
|
|
19860
|
+
var CalibrationResult = external_exports.object({
|
|
19861
|
+
frame: CalibrationFrame,
|
|
19862
|
+
copy: external_exports.string()
|
|
19863
|
+
});
|
|
19864
|
+
var FirstRunCalibration = external_exports.enum(["scan", "floor"]);
|
|
19865
|
+
var SetupHandoffOption = external_exports.object({
|
|
19866
|
+
id: external_exports.enum(["enter-remediation", "open-dashboard", "not-now"]),
|
|
19867
|
+
label: external_exports.string()
|
|
19868
|
+
});
|
|
19869
|
+
var DashboardHandoffOptions = external_exports.tuple([
|
|
19870
|
+
SetupHandoffOption.extend({ id: external_exports.literal("open-dashboard") }),
|
|
19871
|
+
SetupHandoffOption.extend({ id: external_exports.literal("not-now") })
|
|
19872
|
+
]);
|
|
19873
|
+
var ComposedRemediationOptions = external_exports.tuple([
|
|
19874
|
+
SetupHandoffOption.extend({ id: external_exports.literal("enter-remediation") }),
|
|
19875
|
+
SetupHandoffOption.extend({ id: external_exports.literal("open-dashboard") }),
|
|
19876
|
+
SetupHandoffOption.extend({ id: external_exports.literal("not-now") })
|
|
19877
|
+
]);
|
|
19878
|
+
var SetupHandoffOffer = external_exports.object({
|
|
19879
|
+
worthALook: external_exports.number().int().nonnegative(),
|
|
19880
|
+
liveKeys: external_exports.number().int().nonnegative().optional(),
|
|
19881
|
+
options: external_exports.union([DashboardHandoffOptions, ComposedRemediationOptions])
|
|
19882
|
+
}).refine(
|
|
19883
|
+
(o) => o.options.some((opt) => opt.id === "enter-remediation") === (o.liveKeys ?? 0) > 0,
|
|
19884
|
+
{
|
|
19885
|
+
message: "the chain-entry option is present exactly when liveKeys > 0",
|
|
19886
|
+
path: ["options"]
|
|
19887
|
+
}
|
|
19888
|
+
).refine((o) => (o.liveKeys ?? 0) <= o.worthALook, {
|
|
19889
|
+
message: "liveKeys is a subset of worthALook and cannot exceed it",
|
|
19890
|
+
path: ["liveKeys"]
|
|
18378
19891
|
});
|
|
18379
19892
|
|
|
18380
19893
|
// ../packages/schema/src/zod/shares-access.ts
|
|
@@ -18396,7 +19909,7 @@ function deriveReviewReasons(trust, transports) {
|
|
|
18396
19909
|
const reasons = [];
|
|
18397
19910
|
if (trust === "ip") reasons.push("raw_ip");
|
|
18398
19911
|
if (trust === "unverified") reasons.push("unverified_domain");
|
|
18399
|
-
if (transports.includes("http")) reasons.push("plaintext_transport");
|
|
19912
|
+
if (transports.includes("http") || transports.includes("ws")) reasons.push("plaintext_transport");
|
|
18400
19913
|
return reasons;
|
|
18401
19914
|
}
|
|
18402
19915
|
function buildReviewInfo(trust, transports) {
|
|
@@ -18423,139 +19936,6 @@ function reviewSeverityRank(reasons) {
|
|
|
18423
19936
|
return Math.min(...reasons.map((r) => REVIEW_SEVERITY_RANK[r]));
|
|
18424
19937
|
}
|
|
18425
19938
|
|
|
18426
|
-
// ../packages/local-ops/src/fs-scan.ts
|
|
18427
|
-
var import_ignore = __toESM(require_ignore(), 1);
|
|
18428
|
-
var AKAIGNORE_FILENAME = ".akaignore";
|
|
18429
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
18430
|
-
"node_modules",
|
|
18431
|
-
".git",
|
|
18432
|
-
"dist",
|
|
18433
|
-
".next",
|
|
18434
|
-
"coverage",
|
|
18435
|
-
".turbo",
|
|
18436
|
-
"build",
|
|
18437
|
-
"out"
|
|
18438
|
-
]);
|
|
18439
|
-
var MAX_BYTES = 1e6;
|
|
18440
|
-
function readIgnoreLayer(dir, filename) {
|
|
18441
|
-
try {
|
|
18442
|
-
const content = readFileSync2(join2(dir, filename), "utf8");
|
|
18443
|
-
return { base: dir, matcher: (0, import_ignore.default)().add(content) };
|
|
18444
|
-
} catch {
|
|
18445
|
-
return void 0;
|
|
18446
|
-
}
|
|
18447
|
-
}
|
|
18448
|
-
function evaluate(layers, absPath, isDir) {
|
|
18449
|
-
let state = "unmatched";
|
|
18450
|
-
for (const layer of layers) {
|
|
18451
|
-
const rel = relative(layer.base, absPath).split(sep).join("/") + (isDir ? "/" : "");
|
|
18452
|
-
const verdict = layer.matcher.test(rel);
|
|
18453
|
-
if (verdict.ignored) state = "ignored";
|
|
18454
|
-
else if (verdict.unignored) state = "unignored";
|
|
18455
|
-
}
|
|
18456
|
-
return state;
|
|
18457
|
-
}
|
|
18458
|
-
function* collectFiles(target) {
|
|
18459
|
-
let st;
|
|
18460
|
-
try {
|
|
18461
|
-
st = statSync(target);
|
|
18462
|
-
} catch {
|
|
18463
|
-
return;
|
|
18464
|
-
}
|
|
18465
|
-
if (st.isFile()) {
|
|
18466
|
-
if (st.size <= MAX_BYTES) yield { path: target, gitignored: false };
|
|
18467
|
-
return;
|
|
18468
|
-
}
|
|
18469
|
-
if (!st.isDirectory()) return;
|
|
18470
|
-
yield* visit(target, [], [], false);
|
|
18471
|
-
}
|
|
18472
|
-
function* visit(dir, markLayers, skipLayers, inIgnoredDir) {
|
|
18473
|
-
const markLayer = inIgnoredDir ? void 0 : readIgnoreLayer(dir, ".gitignore");
|
|
18474
|
-
const dirMarkLayers = markLayer ? [...markLayers, markLayer] : markLayers;
|
|
18475
|
-
const skipLayer = readIgnoreLayer(dir, AKAIGNORE_FILENAME);
|
|
18476
|
-
const dirSkipLayers = skipLayer ? [...skipLayers, skipLayer] : skipLayers;
|
|
18477
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
18478
|
-
const path = join2(dir, entry.name);
|
|
18479
|
-
if (entry.isDirectory()) {
|
|
18480
|
-
const skipState = evaluate(dirSkipLayers, path, true);
|
|
18481
|
-
if (skipState !== "unignored" && (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".") || skipState === "ignored")) {
|
|
18482
|
-
continue;
|
|
18483
|
-
}
|
|
18484
|
-
const dirIgnored = inIgnoredDir || evaluate(dirMarkLayers, path, true) === "ignored";
|
|
18485
|
-
yield* visit(path, dirMarkLayers, dirSkipLayers, dirIgnored);
|
|
18486
|
-
} else if (entry.isFile()) {
|
|
18487
|
-
if (evaluate(dirSkipLayers, path, false) === "ignored") continue;
|
|
18488
|
-
let size;
|
|
18489
|
-
try {
|
|
18490
|
-
size = statSync(path).size;
|
|
18491
|
-
} catch {
|
|
18492
|
-
continue;
|
|
18493
|
-
}
|
|
18494
|
-
if (size > MAX_BYTES) continue;
|
|
18495
|
-
yield {
|
|
18496
|
-
path,
|
|
18497
|
-
gitignored: inIgnoredDir || evaluate(dirMarkLayers, path, false) === "ignored"
|
|
18498
|
-
};
|
|
18499
|
-
}
|
|
18500
|
-
}
|
|
18501
|
-
}
|
|
18502
|
-
function scanPathIntoStore(db, target, opts = {}) {
|
|
18503
|
-
let scanned = 0;
|
|
18504
|
-
let findingCount = 0;
|
|
18505
|
-
const files = [];
|
|
18506
|
-
for (const { path: file2, gitignored } of collectFiles(target)) {
|
|
18507
|
-
let text;
|
|
18508
|
-
try {
|
|
18509
|
-
text = readFileSync2(file2, "utf8");
|
|
18510
|
-
} catch {
|
|
18511
|
-
continue;
|
|
18512
|
-
}
|
|
18513
|
-
scanned++;
|
|
18514
|
-
const matches = scan(text, opts.rules);
|
|
18515
|
-
if (matches.length === 0) continue;
|
|
18516
|
-
const eventId = randomUUID();
|
|
18517
|
-
const metadata = { filePath: file2 };
|
|
18518
|
-
if (gitignored) metadata.gitignored = true;
|
|
18519
|
-
const event = {
|
|
18520
|
-
id: eventId,
|
|
18521
|
-
sourceTool: opts.sourceTool ?? "cli",
|
|
18522
|
-
kind: "code_change",
|
|
18523
|
-
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18524
|
-
contentHash: createHash("sha256").update(text).digest("hex"),
|
|
18525
|
-
content: redact(text, matches),
|
|
18526
|
-
// store the REDACTED file, never the raw secret
|
|
18527
|
-
metadata
|
|
18528
|
-
};
|
|
18529
|
-
const findings = matches.map((m) => ({
|
|
18530
|
-
id: randomUUID(),
|
|
18531
|
-
eventId,
|
|
18532
|
-
ruleId: m.ruleId,
|
|
18533
|
-
category: m.category,
|
|
18534
|
-
severity: m.severity,
|
|
18535
|
-
span: m.span,
|
|
18536
|
-
maskedMatch: maskMatch(m.rawMatch),
|
|
18537
|
-
// Per-pack action (monitor-by-default) when the installed snapshot supplies
|
|
18538
|
-
// one, else the per-category fallback — mirrors the live path's resolveAction.
|
|
18539
|
-
actionTaken: opts.ruleActions?.get(m.ruleId) ?? DEFAULT_ACTIONS[m.category],
|
|
18540
|
-
confidence: m.confidence
|
|
18541
|
-
}));
|
|
18542
|
-
db.recordCapture(event, findings);
|
|
18543
|
-
files.push({ path: file2, gitignored, findings });
|
|
18544
|
-
findingCount += findings.length;
|
|
18545
|
-
}
|
|
18546
|
-
return { scanned, findings: findingCount, files };
|
|
18547
|
-
}
|
|
18548
|
-
|
|
18549
|
-
// ../packages/local-ops/src/project-inventory.ts
|
|
18550
|
-
import { statSync as statSync5 } from "fs";
|
|
18551
|
-
import { resolve } from "path";
|
|
18552
|
-
|
|
18553
|
-
// ../packages/persistence/src/database.ts
|
|
18554
|
-
import { randomUUID as randomUUID9 } from "crypto";
|
|
18555
|
-
import { existsSync as existsSync2, renameSync, rmSync } from "fs";
|
|
18556
|
-
import { join as join3, sep as sep2 } from "path";
|
|
18557
|
-
import { DatabaseSync } from "node:sqlite";
|
|
18558
|
-
|
|
18559
19939
|
// ../packages/persistence/src/internal/sql-text.ts
|
|
18560
19940
|
function escapeLikePattern(s) {
|
|
18561
19941
|
return s.replace(/\\/g, "\\\\").replace(/%/g, "\\%").replace(/_/g, "\\_");
|
|
@@ -18653,9 +20033,9 @@ function evidenceExists(db, object2) {
|
|
|
18653
20033
|
}
|
|
18654
20034
|
|
|
18655
20035
|
// ../packages/persistence/src/ids.ts
|
|
18656
|
-
import { createHash
|
|
20036
|
+
import { createHash } from "crypto";
|
|
18657
20037
|
function sha256Hex(input) {
|
|
18658
|
-
return
|
|
20038
|
+
return createHash("sha256").update(input).digest("hex");
|
|
18659
20039
|
}
|
|
18660
20040
|
function inventoryId(objectType, identityKey) {
|
|
18661
20041
|
return sha256Hex(canonicalIdentity(["inventory", objectType, identityKey]));
|
|
@@ -18749,6 +20129,7 @@ function applyMigrations(db) {
|
|
|
18749
20129
|
ensureSyncedAtColumn(db, "audit_events");
|
|
18750
20130
|
ensureScanLedgerTable(db);
|
|
18751
20131
|
ensureBlockedDetectionsTable(db);
|
|
20132
|
+
ensureRuleProbeCacheTable(db);
|
|
18752
20133
|
ensureWriteGateTrigger(db);
|
|
18753
20134
|
ensureTokenUsageColumns(db);
|
|
18754
20135
|
reconcileSourceProjectIds(db);
|
|
@@ -18888,6 +20269,14 @@ function ensureBlockedDetectionsTable(db) {
|
|
|
18888
20269
|
blocked_at INTEGER NOT NULL
|
|
18889
20270
|
)`);
|
|
18890
20271
|
}
|
|
20272
|
+
function ensureRuleProbeCacheTable(db) {
|
|
20273
|
+
db.exec(`CREATE TABLE IF NOT EXISTS rule_probe_cache (
|
|
20274
|
+
rule_key TEXT PRIMARY KEY,
|
|
20275
|
+
verdict TEXT NOT NULL,
|
|
20276
|
+
worst_probe_ms REAL NOT NULL,
|
|
20277
|
+
checked_at INTEGER NOT NULL
|
|
20278
|
+
)`);
|
|
20279
|
+
}
|
|
18891
20280
|
|
|
18892
20281
|
// ../packages/persistence/src/paths.ts
|
|
18893
20282
|
import { chmodSync, mkdirSync } from "fs";
|
|
@@ -20109,7 +21498,7 @@ var SqliteEventsRepository = class {
|
|
|
20109
21498
|
};
|
|
20110
21499
|
|
|
20111
21500
|
// ../packages/persistence/src/repositories/exceptions.ts
|
|
20112
|
-
import { randomUUID
|
|
21501
|
+
import { randomUUID } from "crypto";
|
|
20113
21502
|
|
|
20114
21503
|
// ../packages/persistence/src/internal/sqlite-errors.ts
|
|
20115
21504
|
var SQLITE_CONSTRAINT_UNIQUE = 2067;
|
|
@@ -20187,7 +21576,7 @@ var SqliteExceptionsRepository = class {
|
|
|
20187
21576
|
"provider conditions are not supported yet \u2014 a grant with one would never apply"
|
|
20188
21577
|
);
|
|
20189
21578
|
}
|
|
20190
|
-
const id =
|
|
21579
|
+
const id = randomUUID();
|
|
20191
21580
|
const now = Date.now();
|
|
20192
21581
|
try {
|
|
20193
21582
|
this.insertExceptionRow(id, input, now);
|
|
@@ -20892,7 +22281,7 @@ var SqliteInspectionFindingsRepository = class {
|
|
|
20892
22281
|
};
|
|
20893
22282
|
|
|
20894
22283
|
// ../packages/persistence/src/repositories/installed-packs.ts
|
|
20895
|
-
import { createHash as
|
|
22284
|
+
import { createHash as createHash2, randomUUID as randomUUID2 } from "crypto";
|
|
20896
22285
|
|
|
20897
22286
|
// ../packages/persistence/src/semver.ts
|
|
20898
22287
|
function parse4(version2) {
|
|
@@ -20942,7 +22331,7 @@ function inventorySignature(packs2) {
|
|
|
20942
22331
|
return packs2.map((p) => `${p.namespace}/${p.packId}@${p.version}#${hashRules(p.rulesJson)}`).sort().join(",");
|
|
20943
22332
|
}
|
|
20944
22333
|
function hashRules(rulesJson) {
|
|
20945
|
-
return
|
|
22334
|
+
return createHash2("sha1").update(rulesJson).digest("hex");
|
|
20946
22335
|
}
|
|
20947
22336
|
function parseVersion(v) {
|
|
20948
22337
|
const m = /^(\d+)\.(\d+)\.(\d+)/.exec(v);
|
|
@@ -21043,7 +22432,7 @@ var SqliteInstalledPacksRepository = class {
|
|
|
21043
22432
|
let behind = false;
|
|
21044
22433
|
for (const row of rows) {
|
|
21045
22434
|
const params = {
|
|
21046
|
-
id:
|
|
22435
|
+
id: randomUUID2(),
|
|
21047
22436
|
namespace: row.namespace,
|
|
21048
22437
|
packId: row.packId,
|
|
21049
22438
|
version: row.version,
|
|
@@ -21055,7 +22444,7 @@ var SqliteInstalledPacksRepository = class {
|
|
|
21055
22444
|
if (stored === void 0 || !isMirrorDowngrade(row, stored)) {
|
|
21056
22445
|
this.upsertAvailableStmt.run({
|
|
21057
22446
|
...params,
|
|
21058
|
-
id:
|
|
22447
|
+
id: randomUUID2(),
|
|
21059
22448
|
recordedBy: meta3?.recordedBy ?? null
|
|
21060
22449
|
});
|
|
21061
22450
|
} else {
|
|
@@ -21376,7 +22765,7 @@ var SqliteInventoryRepository = class {
|
|
|
21376
22765
|
};
|
|
21377
22766
|
|
|
21378
22767
|
// ../packages/persistence/src/repositories/inventory-assets.ts
|
|
21379
|
-
import { randomUUID as
|
|
22768
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
21380
22769
|
var CATEGORY_ORDER = ["config", "skill", "mcp", "hook"];
|
|
21381
22770
|
var VALID_HARNESS_IDS = new Set(HarnessId.options);
|
|
21382
22771
|
var WORKTREE_CHECKOUT_FILTER = "(url IS NULL OR (url NOT LIKE '%/.claude/worktrees/%' AND url NOT LIKE '%\\.claude\\worktrees\\%'))";
|
|
@@ -21856,7 +23245,7 @@ var SqliteInventoryAssetsRepository = class {
|
|
|
21856
23245
|
`INSERT INTO file_access_override (id, project_id, path, access, created_at, updated_at)
|
|
21857
23246
|
VALUES (:id, :projectId, :path, :access, :now, :now)
|
|
21858
23247
|
ON CONFLICT (project_id, path) DO UPDATE SET access = excluded.access, updated_at = excluded.updated_at`
|
|
21859
|
-
).run({ id:
|
|
23248
|
+
).run({ id: randomUUID3(), projectId, path, access, now: Date.now() });
|
|
21860
23249
|
}
|
|
21861
23250
|
return true;
|
|
21862
23251
|
}
|
|
@@ -21877,7 +23266,7 @@ var SqliteInventoryAssetsRepository = class {
|
|
|
21877
23266
|
`INSERT INTO mcp_trust_override (id, asset_id, trust, created_at, updated_at)
|
|
21878
23267
|
VALUES (:id, :assetId, :trust, :now, :now)
|
|
21879
23268
|
ON CONFLICT (asset_id) DO UPDATE SET trust = excluded.trust, updated_at = excluded.updated_at`
|
|
21880
|
-
).run({ id:
|
|
23269
|
+
).run({ id: randomUUID3(), assetId, trust, now: Date.now() });
|
|
21881
23270
|
}
|
|
21882
23271
|
this.configRowsCache = void 0;
|
|
21883
23272
|
return "ok";
|
|
@@ -22174,7 +23563,7 @@ var SqliteInventoryAssetsRepository = class {
|
|
|
22174
23563
|
};
|
|
22175
23564
|
|
|
22176
23565
|
// ../packages/persistence/src/repositories/policies.ts
|
|
22177
|
-
import { randomUUID as
|
|
23566
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
22178
23567
|
var SqlitePoliciesRepository = class {
|
|
22179
23568
|
constructor(db) {
|
|
22180
23569
|
this.db = db;
|
|
@@ -22209,7 +23598,7 @@ var SqlitePoliciesRepository = class {
|
|
|
22209
23598
|
failOpenTransaction(this.db, () => {
|
|
22210
23599
|
for (const [category, action] of Object.entries(DEFAULT_ACTIONS)) {
|
|
22211
23600
|
stmt.run({
|
|
22212
|
-
id:
|
|
23601
|
+
id: randomUUID4(),
|
|
22213
23602
|
target: JSON.stringify({ category }),
|
|
22214
23603
|
action,
|
|
22215
23604
|
now: Date.now()
|
|
@@ -22229,7 +23618,7 @@ var SqlitePoliciesRepository = class {
|
|
|
22229
23618
|
`INSERT INTO policies (id, scope, target, action, enabled, created_at, updated_at)
|
|
22230
23619
|
VALUES (:id, 'global', :target, :action, 1, :now, :now)
|
|
22231
23620
|
ON CONFLICT(scope, target) DO UPDATE SET action = excluded.action, enabled = 1, updated_at = excluded.updated_at`
|
|
22232
|
-
).run({ id:
|
|
23621
|
+
).run({ id: randomUUID4(), target: JSON.stringify({ category }), action, now });
|
|
22233
23622
|
}
|
|
22234
23623
|
// Caps every global per-category policy currently set to block/redact down
|
|
22235
23624
|
// to warn (see warn-era-cap.ts). Rule-targeted policies are untouched.
|
|
@@ -22297,7 +23686,7 @@ var SqlitePolicyCatalogRepository = class {
|
|
|
22297
23686
|
};
|
|
22298
23687
|
|
|
22299
23688
|
// ../packages/persistence/src/repositories/project-files.ts
|
|
22300
|
-
import { randomUUID as
|
|
23689
|
+
import { randomUUID as randomUUID5 } from "crypto";
|
|
22301
23690
|
var SqliteProjectFilesRepository = class {
|
|
22302
23691
|
constructor(db) {
|
|
22303
23692
|
this.db = db;
|
|
@@ -22329,7 +23718,7 @@ var SqliteProjectFilesRepository = class {
|
|
|
22329
23718
|
const stamp = Math.max(now, maxStamp + 1);
|
|
22330
23719
|
for (const file2 of scan2.files) {
|
|
22331
23720
|
this.upsertStmt.run({
|
|
22332
|
-
id:
|
|
23721
|
+
id: randomUUID5(),
|
|
22333
23722
|
projectId,
|
|
22334
23723
|
path: file2.path,
|
|
22335
23724
|
name: file2.name,
|
|
@@ -22343,7 +23732,7 @@ var SqliteProjectFilesRepository = class {
|
|
|
22343
23732
|
};
|
|
22344
23733
|
|
|
22345
23734
|
// ../packages/persistence/src/repositories/resolutions.ts
|
|
22346
|
-
import { randomUUID as
|
|
23735
|
+
import { randomUUID as randomUUID6 } from "crypto";
|
|
22347
23736
|
var SqliteResolutionsRepository = class {
|
|
22348
23737
|
constructor(db, now = () => Date.now()) {
|
|
22349
23738
|
this.db = db;
|
|
@@ -22397,7 +23786,7 @@ var SqliteResolutionsRepository = class {
|
|
|
22397
23786
|
*/
|
|
22398
23787
|
insertResolution(r) {
|
|
22399
23788
|
this.insertStmt.run({
|
|
22400
|
-
id:
|
|
23789
|
+
id: randomUUID6(),
|
|
22401
23790
|
findingKey: r.findingKey,
|
|
22402
23791
|
status: FindingStatus.parse(r.status),
|
|
22403
23792
|
method: ResolutionMethod.parse(r.method),
|
|
@@ -22441,6 +23830,35 @@ var SqliteResolutionsRepository = class {
|
|
|
22441
23830
|
}
|
|
22442
23831
|
};
|
|
22443
23832
|
|
|
23833
|
+
// ../packages/persistence/src/repositories/rule-probe-cache.ts
|
|
23834
|
+
var SqliteRuleProbeCacheRepository = class {
|
|
23835
|
+
constructor(db) {
|
|
23836
|
+
this.db = db;
|
|
23837
|
+
this.upsertStmt = db.prepare(
|
|
23838
|
+
`INSERT INTO rule_probe_cache (rule_key, verdict, worst_probe_ms, checked_at)
|
|
23839
|
+
VALUES (:ruleKey, :verdict, :worstProbeMs, :checkedAt)
|
|
23840
|
+
ON CONFLICT (rule_key) DO UPDATE SET
|
|
23841
|
+
verdict = excluded.verdict,
|
|
23842
|
+
worst_probe_ms = excluded.worst_probe_ms,
|
|
23843
|
+
checked_at = excluded.checked_at`
|
|
23844
|
+
);
|
|
23845
|
+
this.readStmt = db.prepare(
|
|
23846
|
+
`SELECT verdict, worst_probe_ms AS worstProbeMs FROM rule_probe_cache WHERE rule_key = :ruleKey`
|
|
23847
|
+
);
|
|
23848
|
+
}
|
|
23849
|
+
db;
|
|
23850
|
+
upsertStmt;
|
|
23851
|
+
readStmt;
|
|
23852
|
+
getVerdict(ruleKey) {
|
|
23853
|
+
return getRow(this.readStmt, { ruleKey });
|
|
23854
|
+
}
|
|
23855
|
+
setVerdict(ruleKey, verdict, worstProbeMs) {
|
|
23856
|
+
failOpenTransaction(this.db, () => {
|
|
23857
|
+
this.upsertStmt.run({ ruleKey, verdict, worstProbeMs, checkedAt: Date.now() });
|
|
23858
|
+
});
|
|
23859
|
+
}
|
|
23860
|
+
};
|
|
23861
|
+
|
|
22444
23862
|
// ../packages/persistence/src/repositories/scan-ledger.ts
|
|
22445
23863
|
var SqliteScanLedgerRepository = class {
|
|
22446
23864
|
constructor(db) {
|
|
@@ -22851,12 +24269,51 @@ var SqliteSecurityRepository = class {
|
|
|
22851
24269
|
};
|
|
22852
24270
|
|
|
22853
24271
|
// ../packages/persistence/src/repositories/shares.ts
|
|
22854
|
-
import { randomUUID as
|
|
22855
|
-
var
|
|
24272
|
+
import { randomUUID as randomUUID7 } from "crypto";
|
|
24273
|
+
var MAX_EGRESS_CALL_SITES_PER_PROJECT = 5e3;
|
|
24274
|
+
var IN_CHUNK = 500;
|
|
24275
|
+
var KIND_ORDER = ["provider", "internal", "external", "ip"];
|
|
24276
|
+
var PLAINTEXT_TRANSPORT_SQL = "('http', 'ws')";
|
|
24277
|
+
var OVERRIDE_JOIN = `LEFT JOIN egress_decision_override oh ON oh.host = d.host
|
|
24278
|
+
LEFT JOIN egress_decision_override ol ON ol.destination_id = d.id AND ol.host IS NULL`;
|
|
22856
24279
|
var CALL_SITE_EMBED_CAP = 200;
|
|
22857
24280
|
function parseNetwork(networkJson) {
|
|
22858
24281
|
return safeJson(networkJson, null);
|
|
22859
24282
|
}
|
|
24283
|
+
function capHits(all, mode) {
|
|
24284
|
+
if (all.length <= MAX_EGRESS_CALL_SITES_PER_PROJECT) {
|
|
24285
|
+
return { hits: [...all], droppedFiles: [], truncated: false };
|
|
24286
|
+
}
|
|
24287
|
+
if (mode === "walk") {
|
|
24288
|
+
return {
|
|
24289
|
+
hits: all.slice(0, MAX_EGRESS_CALL_SITES_PER_PROJECT),
|
|
24290
|
+
droppedFiles: [],
|
|
24291
|
+
truncated: true
|
|
24292
|
+
};
|
|
24293
|
+
}
|
|
24294
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
24295
|
+
for (const hit of all) {
|
|
24296
|
+
const bucket = byFile.get(hit.site.file);
|
|
24297
|
+
if (bucket === void 0) byFile.set(hit.site.file, [hit]);
|
|
24298
|
+
else bucket.push(hit);
|
|
24299
|
+
}
|
|
24300
|
+
const hits = [];
|
|
24301
|
+
const droppedFiles = [];
|
|
24302
|
+
for (const [file2, bucket] of byFile) {
|
|
24303
|
+
if (hits.length + bucket.length > MAX_EGRESS_CALL_SITES_PER_PROJECT) droppedFiles.push(file2);
|
|
24304
|
+
else hits.push(...bucket);
|
|
24305
|
+
}
|
|
24306
|
+
return { hits, droppedFiles, truncated: true };
|
|
24307
|
+
}
|
|
24308
|
+
function withoutDroppedFiles(reconcile, droppedFiles) {
|
|
24309
|
+
if (reconcile.mode === "walk" || droppedFiles.length === 0) return reconcile;
|
|
24310
|
+
const dropped = new Set(droppedFiles);
|
|
24311
|
+
return {
|
|
24312
|
+
mode: "ledger",
|
|
24313
|
+
scannedFiles: reconcile.scannedFiles.filter((file2) => !dropped.has(file2)),
|
|
24314
|
+
deletedFiles: reconcile.deletedFiles.filter((file2) => !dropped.has(file2))
|
|
24315
|
+
};
|
|
24316
|
+
}
|
|
22860
24317
|
function toEndpointSummary(row) {
|
|
22861
24318
|
return {
|
|
22862
24319
|
id: row.id,
|
|
@@ -22947,13 +24404,15 @@ var SqliteSharesRepository = class {
|
|
|
22947
24404
|
const callSites = countScalar(this.db, "SELECT count(*) AS n FROM share_call_site");
|
|
22948
24405
|
const insecure = countScalar(
|
|
22949
24406
|
this.db,
|
|
22950
|
-
|
|
24407
|
+
`SELECT count(DISTINCT destination_id) AS n FROM share_endpoint
|
|
24408
|
+
WHERE transport IN ${PLAINTEXT_TRANSPORT_SQL}`
|
|
22951
24409
|
);
|
|
22952
24410
|
const needsReview = countScalar(
|
|
22953
24411
|
this.db,
|
|
22954
24412
|
`SELECT count(DISTINCT d.id) AS n
|
|
22955
24413
|
FROM share_destination d
|
|
22956
|
-
LEFT JOIN share_endpoint e ON e.destination_id = d.id
|
|
24414
|
+
LEFT JOIN share_endpoint e ON e.destination_id = d.id
|
|
24415
|
+
AND e.transport IN ${PLAINTEXT_TRANSPORT_SQL}
|
|
22957
24416
|
WHERE d.trust IN ('unverified', 'ip') OR e.id IS NOT NULL`
|
|
22958
24417
|
);
|
|
22959
24418
|
const kindCounts = countBy(
|
|
@@ -22963,6 +24422,7 @@ var SqliteSharesRepository = class {
|
|
|
22963
24422
|
const byKind = {
|
|
22964
24423
|
provider: kindCounts.get("provider") ?? 0,
|
|
22965
24424
|
internal: kindCounts.get("internal") ?? 0,
|
|
24425
|
+
external: kindCounts.get("external") ?? 0,
|
|
22966
24426
|
ip: kindCounts.get("ip") ?? 0
|
|
22967
24427
|
};
|
|
22968
24428
|
const trustCounts = countBy(
|
|
@@ -23038,23 +24498,316 @@ var SqliteSharesRepository = class {
|
|
|
23038
24498
|
// real edit from a no-such-destination.
|
|
23039
24499
|
/**
|
|
23040
24500
|
* Set (decision) or clear (null) the egress decision override for a destination.
|
|
23041
|
-
* `null` deletes the override
|
|
24501
|
+
* `null` deletes the override rows → reverts to the trust default.
|
|
24502
|
+
*
|
|
24503
|
+
* The written row carries both the destination id and its host, so the
|
|
24504
|
+
* decision re-attaches by host after the destination is pruned and
|
|
24505
|
+
* re-detected under a fresh id. Rows written before the host column existed
|
|
24506
|
+
* (host NULL, matched by destination id) are replaced rather than left to
|
|
24507
|
+
* shadow the new one. Runs IMMEDIATE: the host lookup is read-then-write and
|
|
24508
|
+
* would otherwise race a concurrent prune.
|
|
23042
24509
|
*/
|
|
23043
24510
|
setEgressDecision(destinationId, decision) {
|
|
23044
|
-
|
|
23045
|
-
|
|
23046
|
-
|
|
23047
|
-
|
|
23048
|
-
|
|
24511
|
+
let existed = false;
|
|
24512
|
+
withTransaction(
|
|
24513
|
+
this.db,
|
|
24514
|
+
() => {
|
|
24515
|
+
const dest = this.db.prepare("SELECT host FROM share_destination WHERE id = ?").get(destinationId);
|
|
24516
|
+
if (dest === void 0) return;
|
|
24517
|
+
existed = true;
|
|
24518
|
+
this.db.prepare(
|
|
24519
|
+
`DELETE FROM egress_decision_override
|
|
24520
|
+
WHERE host = :host OR (destination_id = :destinationId AND host IS NULL)`
|
|
24521
|
+
).run({ host: dest.host, destinationId });
|
|
24522
|
+
if (decision === null) return;
|
|
24523
|
+
this.db.prepare(
|
|
24524
|
+
`INSERT INTO egress_decision_override
|
|
24525
|
+
(id, destination_id, host, decision, created_at, updated_at)
|
|
24526
|
+
VALUES (:id, :destinationId, :host, :decision, :now, :now)`
|
|
24527
|
+
).run({
|
|
24528
|
+
id: randomUUID7(),
|
|
24529
|
+
destinationId,
|
|
24530
|
+
host: dest.host,
|
|
24531
|
+
decision,
|
|
24532
|
+
now: Date.now()
|
|
24533
|
+
});
|
|
24534
|
+
},
|
|
24535
|
+
"IMMEDIATE"
|
|
24536
|
+
);
|
|
24537
|
+
return existed;
|
|
24538
|
+
}
|
|
24539
|
+
/**
|
|
24540
|
+
* Record one project's statically-extracted egress: reconcile the previously
|
|
24541
|
+
* stored call sites against this scan, upsert destination → endpoint → call
|
|
24542
|
+
* site for every hit, confirm `last_seen` on everything the project still
|
|
24543
|
+
* references, and drop what no longer has evidence.
|
|
24544
|
+
*
|
|
24545
|
+
* Reconciliation keys on `projectKey` alone; `project` and `projectId` are
|
|
24546
|
+
* display payload and never scope a delete. The whole write is one
|
|
24547
|
+
* transaction: a failure leaves the project's previous inventory exactly as
|
|
24548
|
+
* it was, and THROWS rather than reporting a partial write — callers decide
|
|
24549
|
+
* their own fail-open behavior, and the scanner additionally withholds its
|
|
24550
|
+
* ledger commit so the next scan retries.
|
|
24551
|
+
*
|
|
24552
|
+
* Over-cap input is truncated at a FILE boundary, and the files that lost
|
|
24553
|
+
* their hits are both excluded from the reconcile delete and named in
|
|
24554
|
+
* `droppedFiles`. That pairing is what keeps truncation non-destructive on
|
|
24555
|
+
* the ledger path: a dropped file keeps whatever rows it already had, and its
|
|
24556
|
+
* caller withholds the ledger entry so the next scan reads it again.
|
|
24557
|
+
*/
|
|
24558
|
+
recordProjectEgress(input) {
|
|
24559
|
+
const { hits, droppedFiles, truncated } = capHits(input.hits, input.reconcile.mode);
|
|
24560
|
+
const reconcile = withoutDroppedFiles(input.reconcile, droppedFiles);
|
|
24561
|
+
const now = Date.now();
|
|
24562
|
+
let summary = {
|
|
24563
|
+
destinations: 0,
|
|
24564
|
+
endpoints: 0,
|
|
24565
|
+
callSites: 0,
|
|
24566
|
+
truncated,
|
|
24567
|
+
droppedFiles
|
|
24568
|
+
};
|
|
24569
|
+
withTransaction(
|
|
24570
|
+
this.db,
|
|
24571
|
+
() => {
|
|
24572
|
+
const projectId = input.projectId ?? this.knownProjectId(input.projectKey);
|
|
24573
|
+
this.reconcileCallSites(input.projectKey, reconcile);
|
|
24574
|
+
this.upsertHits(input, hits, projectId, now);
|
|
24575
|
+
this.confirmLastSeen(input.projectKey, now);
|
|
24576
|
+
this.pruneOrphans();
|
|
24577
|
+
summary = { ...this.projectTotals(input.projectKey), truncated, droppedFiles };
|
|
24578
|
+
},
|
|
24579
|
+
"IMMEDIATE"
|
|
24580
|
+
);
|
|
24581
|
+
return summary;
|
|
24582
|
+
}
|
|
24583
|
+
// ─── Egress write internals ──────────────────────────────────────────────────
|
|
24584
|
+
/**
|
|
24585
|
+
* Clear the stored call sites this scan is responsible for re-creating.
|
|
24586
|
+
*
|
|
24587
|
+
* Each pipeline may only delete rows its own walker could have produced. The
|
|
24588
|
+
* fs walk behind 'walk' mode never descends into dot-directories, so its
|
|
24589
|
+
* delete excludes dot-path files — those rows are the plugin scanner's to
|
|
24590
|
+
* reconcile, and deleting them here would make the two pipelines erase each
|
|
24591
|
+
* other's rows on every alternating scan. 'ledger' mode names its files
|
|
24592
|
+
* outright and never mass-deletes, so rows the fs walk contributed for files
|
|
24593
|
+
* the scanner skips (vendored, oversize) survive it.
|
|
24594
|
+
*/
|
|
24595
|
+
reconcileCallSites(projectKey, reconcile) {
|
|
24596
|
+
if (reconcile.mode === "walk") {
|
|
24597
|
+
const prefix = reconcile.walkedPrefix.replace(/\/+$/, "");
|
|
24598
|
+
this.db.prepare(
|
|
24599
|
+
`DELETE FROM share_call_site
|
|
24600
|
+
WHERE project_key = :key
|
|
24601
|
+
AND (:prefix = '' OR file = :prefix OR file LIKE :subtree ESCAPE '\\')
|
|
24602
|
+
AND file NOT LIKE '.%'
|
|
24603
|
+
AND file NOT LIKE '%/.%'`
|
|
24604
|
+
).run({ key: projectKey, prefix, subtree: `${escapeLikePattern(prefix)}/%` });
|
|
24605
|
+
return;
|
|
24606
|
+
}
|
|
24607
|
+
const files = [.../* @__PURE__ */ new Set([...reconcile.scannedFiles, ...reconcile.deletedFiles])];
|
|
24608
|
+
for (let i = 0; i < files.length; i += IN_CHUNK) {
|
|
24609
|
+
const chunk = files.slice(i, i + IN_CHUNK);
|
|
24610
|
+
this.db.prepare(
|
|
24611
|
+
`DELETE FROM share_call_site
|
|
24612
|
+
WHERE project_key = ? AND file IN (${placeholders(chunk.length)})`
|
|
24613
|
+
).run(projectKey, ...chunk);
|
|
24614
|
+
}
|
|
24615
|
+
}
|
|
24616
|
+
/**
|
|
24617
|
+
* Upsert every hit as destination → endpoint → call site. Destinations key on
|
|
24618
|
+
* `host` and endpoints on `(destination_id, method, url)`, both shared across
|
|
24619
|
+
* projects; only the call site carries `project_key`. A destination's `note`
|
|
24620
|
+
* is user-owned and never overwritten. The id caches keep one upsert per
|
|
24621
|
+
* distinct host and endpoint, so the first hit for a host supplies its
|
|
24622
|
+
* classification for this batch.
|
|
24623
|
+
*/
|
|
24624
|
+
upsertHits(input, hits, projectId, now) {
|
|
24625
|
+
if (hits.length === 0) return;
|
|
24626
|
+
const destStmt = this.db.prepare(
|
|
24627
|
+
`INSERT INTO share_destination
|
|
24628
|
+
(id, kind, name, host, category, trust, network_json, last_seen, provenance,
|
|
24629
|
+
created_at, updated_at)
|
|
24630
|
+
VALUES (:id, :kind, :name, :host, :category, :trust, :networkJson, :now, 'scan', :now, :now)
|
|
24631
|
+
ON CONFLICT (host) DO UPDATE SET
|
|
24632
|
+
kind = excluded.kind,
|
|
24633
|
+
name = excluded.name,
|
|
24634
|
+
category = excluded.category,
|
|
24635
|
+
trust = excluded.trust,
|
|
24636
|
+
network_json = excluded.network_json,
|
|
24637
|
+
last_seen = excluded.last_seen,
|
|
24638
|
+
updated_at = excluded.updated_at`
|
|
24639
|
+
);
|
|
24640
|
+
const destIdStmt = this.db.prepare("SELECT id FROM share_destination WHERE host = ?");
|
|
24641
|
+
const endpointStmt = this.db.prepare(
|
|
24642
|
+
`INSERT INTO share_endpoint
|
|
24643
|
+
(id, destination_id, method, transport, url, template, data_class, last_seen,
|
|
24644
|
+
created_at, updated_at)
|
|
24645
|
+
VALUES (:id, :destinationId, :method, :transport, :url, :template, :dataClass, :now,
|
|
24646
|
+
:now, :now)
|
|
24647
|
+
ON CONFLICT (destination_id, method, url) DO UPDATE SET
|
|
24648
|
+
transport = excluded.transport,
|
|
24649
|
+
template = excluded.template,
|
|
24650
|
+
data_class = excluded.data_class,
|
|
24651
|
+
last_seen = excluded.last_seen,
|
|
24652
|
+
updated_at = excluded.updated_at`
|
|
24653
|
+
);
|
|
24654
|
+
const endpointIdStmt = this.db.prepare(
|
|
24655
|
+
"SELECT id FROM share_endpoint WHERE destination_id = ? AND method = ? AND url = ?"
|
|
24656
|
+
);
|
|
24657
|
+
const siteStmt = this.db.prepare(
|
|
24658
|
+
`INSERT INTO share_call_site
|
|
24659
|
+
(id, endpoint_id, project, project_key, file, line, snippet, dynamic, vendored,
|
|
24660
|
+
project_id, created_at, updated_at)
|
|
24661
|
+
VALUES (:id, :endpointId, :project, :projectKey, :file, :line, :snippet, :dynamic,
|
|
24662
|
+
:vendored, :projectId, :now, :now)
|
|
24663
|
+
ON CONFLICT (endpoint_id, project_key, file, line) DO UPDATE SET
|
|
24664
|
+
snippet = excluded.snippet,
|
|
24665
|
+
dynamic = excluded.dynamic,
|
|
24666
|
+
vendored = excluded.vendored,
|
|
24667
|
+
project = excluded.project,
|
|
24668
|
+
project_id = COALESCE(excluded.project_id, share_call_site.project_id),
|
|
24669
|
+
updated_at = excluded.updated_at`
|
|
24670
|
+
);
|
|
24671
|
+
const destIds = /* @__PURE__ */ new Map();
|
|
24672
|
+
const endpointIds = /* @__PURE__ */ new Map();
|
|
24673
|
+
for (const hit of hits) {
|
|
24674
|
+
let destinationId = destIds.get(hit.host);
|
|
24675
|
+
if (destinationId === void 0) {
|
|
24676
|
+
destStmt.run({
|
|
24677
|
+
id: randomUUID7(),
|
|
24678
|
+
kind: hit.kind,
|
|
24679
|
+
name: hit.name,
|
|
24680
|
+
host: hit.host,
|
|
24681
|
+
category: hit.category,
|
|
24682
|
+
trust: hit.trust,
|
|
24683
|
+
networkJson: hit.network === null ? null : JSON.stringify(hit.network),
|
|
24684
|
+
now
|
|
24685
|
+
});
|
|
24686
|
+
destinationId = getRow(destIdStmt, [hit.host])?.id ?? "";
|
|
24687
|
+
destIds.set(hit.host, destinationId);
|
|
24688
|
+
}
|
|
24689
|
+
const endpointKey = `${destinationId}\0${hit.method}\0${hit.url}`;
|
|
24690
|
+
let endpointId = endpointIds.get(endpointKey);
|
|
24691
|
+
if (endpointId === void 0) {
|
|
24692
|
+
endpointStmt.run({
|
|
24693
|
+
id: randomUUID7(),
|
|
24694
|
+
destinationId,
|
|
24695
|
+
method: hit.method,
|
|
24696
|
+
transport: hit.transport,
|
|
24697
|
+
url: hit.url,
|
|
24698
|
+
template: boolToInt(hit.template),
|
|
24699
|
+
dataClass: hit.dataClass,
|
|
24700
|
+
now
|
|
24701
|
+
});
|
|
24702
|
+
endpointId = getRow(endpointIdStmt, [destinationId, hit.method, hit.url])?.id ?? "";
|
|
24703
|
+
endpointIds.set(endpointKey, endpointId);
|
|
24704
|
+
}
|
|
24705
|
+
siteStmt.run({
|
|
24706
|
+
id: randomUUID7(),
|
|
24707
|
+
endpointId,
|
|
24708
|
+
project: input.project,
|
|
24709
|
+
projectKey: input.projectKey,
|
|
24710
|
+
file: hit.site.file,
|
|
24711
|
+
line: hit.site.line,
|
|
24712
|
+
snippet: hit.site.snippet,
|
|
24713
|
+
dynamic: boolToInt(hit.site.dynamic),
|
|
24714
|
+
vendored: boolToInt(hit.site.vendored),
|
|
24715
|
+
projectId,
|
|
24716
|
+
now
|
|
24717
|
+
});
|
|
23049
24718
|
}
|
|
24719
|
+
}
|
|
24720
|
+
/**
|
|
24721
|
+
* The source-project id this project's stored call sites already carry, if
|
|
24722
|
+
* any. Only the pipeline that resolves a source project supplies one; the
|
|
24723
|
+
* other passes null and inherits this, so the link stops flapping between a
|
|
24724
|
+
* real id and NULL depending on which pipeline ran last. The value is a
|
|
24725
|
+
* per-project attribute stored redundantly on each row, so any row's is
|
|
24726
|
+
* representative.
|
|
24727
|
+
*/
|
|
24728
|
+
knownProjectId(projectKey) {
|
|
24729
|
+
return getRow(
|
|
24730
|
+
this.db.prepare(
|
|
24731
|
+
`SELECT project_id AS projectId FROM share_call_site
|
|
24732
|
+
WHERE project_key = ? AND project_id IS NOT NULL LIMIT 1`
|
|
24733
|
+
),
|
|
24734
|
+
[projectKey]
|
|
24735
|
+
)?.projectId ?? null;
|
|
24736
|
+
}
|
|
24737
|
+
/**
|
|
24738
|
+
* Stamp `last_seen` on every endpoint and destination this project still
|
|
24739
|
+
* references — including rows the scan preserved rather than re-wrote, so a
|
|
24740
|
+
* ledger-skipped file's references don't decay into "stale" on the page.
|
|
24741
|
+
*/
|
|
24742
|
+
confirmLastSeen(projectKey, now) {
|
|
23050
24743
|
this.db.prepare(
|
|
23051
|
-
`
|
|
23052
|
-
|
|
23053
|
-
|
|
23054
|
-
|
|
23055
|
-
|
|
23056
|
-
|
|
23057
|
-
|
|
24744
|
+
`UPDATE share_endpoint SET last_seen = :now, updated_at = :now
|
|
24745
|
+
WHERE id IN (SELECT DISTINCT endpoint_id FROM share_call_site WHERE project_key = :key)`
|
|
24746
|
+
).run({ now, key: projectKey });
|
|
24747
|
+
this.db.prepare(
|
|
24748
|
+
`UPDATE share_destination SET last_seen = :now, updated_at = :now
|
|
24749
|
+
WHERE id IN (SELECT DISTINCT e.destination_id
|
|
24750
|
+
FROM share_endpoint e
|
|
24751
|
+
JOIN share_call_site c ON c.endpoint_id = e.id
|
|
24752
|
+
WHERE c.project_key = :key)`
|
|
24753
|
+
).run({ now, key: projectKey });
|
|
24754
|
+
}
|
|
24755
|
+
/**
|
|
24756
|
+
* Drop rows left without evidence: endpoints with no call site, then
|
|
24757
|
+
* destinations with no endpoint. Call sites are the only evidence either one
|
|
24758
|
+
* has, so a row that lost its last one belongs to no project any more.
|
|
24759
|
+
*
|
|
24760
|
+
* Overrides are deleted between the two steps, and only the ones written
|
|
24761
|
+
* before the host column existed. Those match a destination by id alone;
|
|
24762
|
+
* because the id link is released on delete rather than cascading, leaving
|
|
24763
|
+
* them would accumulate rows that match neither join arm and that nothing can
|
|
24764
|
+
* reach again. Host-bearing rows deliberately survive — the host is what
|
|
24765
|
+
* re-attaches a user's decision when the destination comes back.
|
|
24766
|
+
*/
|
|
24767
|
+
pruneOrphans() {
|
|
24768
|
+
this.db.exec(
|
|
24769
|
+
`DELETE FROM share_endpoint
|
|
24770
|
+
WHERE NOT EXISTS (SELECT 1 FROM share_call_site c WHERE c.endpoint_id = share_endpoint.id)`
|
|
24771
|
+
);
|
|
24772
|
+
this.db.exec(
|
|
24773
|
+
`DELETE FROM egress_decision_override
|
|
24774
|
+
WHERE host IS NULL
|
|
24775
|
+
AND destination_id IN (
|
|
24776
|
+
SELECT d.id FROM share_destination d
|
|
24777
|
+
WHERE NOT EXISTS (SELECT 1 FROM share_endpoint e WHERE e.destination_id = d.id))`
|
|
24778
|
+
);
|
|
24779
|
+
this.db.exec(
|
|
24780
|
+
`DELETE FROM share_destination
|
|
24781
|
+
WHERE NOT EXISTS (
|
|
24782
|
+
SELECT 1 FROM share_endpoint e WHERE e.destination_id = share_destination.id)`
|
|
24783
|
+
);
|
|
24784
|
+
}
|
|
24785
|
+
/**
|
|
24786
|
+
* Live totals for one project. Destinations and endpoints are shared across
|
|
24787
|
+
* projects and carry no project column, so both are counted through the call
|
|
24788
|
+
* sites that reference them.
|
|
24789
|
+
*/
|
|
24790
|
+
projectTotals(projectKey) {
|
|
24791
|
+
return {
|
|
24792
|
+
destinations: countScalar(
|
|
24793
|
+
this.db,
|
|
24794
|
+
`SELECT count(DISTINCT e.destination_id) AS n
|
|
24795
|
+
FROM share_endpoint e
|
|
24796
|
+
JOIN share_call_site c ON c.endpoint_id = e.id
|
|
24797
|
+
WHERE c.project_key = ?`,
|
|
24798
|
+
[projectKey]
|
|
24799
|
+
),
|
|
24800
|
+
endpoints: countScalar(
|
|
24801
|
+
this.db,
|
|
24802
|
+
"SELECT count(DISTINCT endpoint_id) AS n FROM share_call_site WHERE project_key = ?",
|
|
24803
|
+
[projectKey]
|
|
24804
|
+
),
|
|
24805
|
+
callSites: countScalar(
|
|
24806
|
+
this.db,
|
|
24807
|
+
"SELECT count(*) AS n FROM share_call_site WHERE project_key = ?",
|
|
24808
|
+
[projectKey]
|
|
24809
|
+
)
|
|
24810
|
+
};
|
|
23058
24811
|
}
|
|
23059
24812
|
// ─── Raw fetchers ────────────────────────────────────────────────────────────
|
|
23060
24813
|
mapDestRow(r) {
|
|
@@ -23074,7 +24827,8 @@ var SqliteSharesRepository = class {
|
|
|
23074
24827
|
fetchDestinations(q, kinds, reviewOnly = false) {
|
|
23075
24828
|
const cols = `d.id, d.kind, d.name, d.host, d.category, d.trust, d.note,
|
|
23076
24829
|
d.network_json AS networkJson, d.last_seen AS lastSeenMs,
|
|
23077
|
-
d.created_at AS createdAt,
|
|
24830
|
+
d.created_at AS createdAt,
|
|
24831
|
+
COALESCE(oh.decision, ol.decision) AS overrideDecision`;
|
|
23078
24832
|
const conditions = [];
|
|
23079
24833
|
const params = [];
|
|
23080
24834
|
if (kinds && kinds.length > 0) {
|
|
@@ -23085,7 +24839,8 @@ var SqliteSharesRepository = class {
|
|
|
23085
24839
|
conditions.push(
|
|
23086
24840
|
`(d.trust IN ('unverified', 'ip')
|
|
23087
24841
|
OR EXISTS (SELECT 1 FROM share_endpoint re
|
|
23088
|
-
WHERE re.destination_id = d.id
|
|
24842
|
+
WHERE re.destination_id = d.id
|
|
24843
|
+
AND re.transport IN ${PLAINTEXT_TRANSPORT_SQL}))`
|
|
23089
24844
|
);
|
|
23090
24845
|
}
|
|
23091
24846
|
let sql;
|
|
@@ -23098,7 +24853,7 @@ var SqliteSharesRepository = class {
|
|
|
23098
24853
|
params.push(pattern, pattern, pattern, pattern, pattern);
|
|
23099
24854
|
sql = `SELECT DISTINCT ${cols}
|
|
23100
24855
|
FROM share_destination d
|
|
23101
|
-
|
|
24856
|
+
${OVERRIDE_JOIN}
|
|
23102
24857
|
LEFT JOIN share_endpoint e ON e.destination_id = d.id
|
|
23103
24858
|
LEFT JOIN share_call_site c ON c.endpoint_id = e.id
|
|
23104
24859
|
${conditions.length ? `WHERE ${conditions.join(" AND ")}` : ""}
|
|
@@ -23106,7 +24861,7 @@ var SqliteSharesRepository = class {
|
|
|
23106
24861
|
} else {
|
|
23107
24862
|
sql = `SELECT ${cols}
|
|
23108
24863
|
FROM share_destination d
|
|
23109
|
-
|
|
24864
|
+
${OVERRIDE_JOIN}
|
|
23110
24865
|
${conditions.length ? `WHERE ${conditions.join(" AND ")}` : ""}
|
|
23111
24866
|
ORDER BY d.created_at ASC, d.id ASC`;
|
|
23112
24867
|
}
|
|
@@ -23121,9 +24876,9 @@ var SqliteSharesRepository = class {
|
|
|
23121
24876
|
this.db.prepare(
|
|
23122
24877
|
`SELECT d.id, d.kind, d.name, d.host, d.category, d.trust, d.note,
|
|
23123
24878
|
d.network_json AS networkJson, d.last_seen AS lastSeenMs,
|
|
23124
|
-
|
|
24879
|
+
COALESCE(oh.decision, ol.decision) AS overrideDecision
|
|
23125
24880
|
FROM share_destination d
|
|
23126
|
-
|
|
24881
|
+
${OVERRIDE_JOIN}
|
|
23127
24882
|
WHERE d.id = ?`
|
|
23128
24883
|
),
|
|
23129
24884
|
[destinationId]
|
|
@@ -23333,7 +25088,7 @@ function backupLegacyStore(file2) {
|
|
|
23333
25088
|
}
|
|
23334
25089
|
function openLocalDatabase(dir) {
|
|
23335
25090
|
ensureDataDirSync(dir);
|
|
23336
|
-
const file2 =
|
|
25091
|
+
const file2 = join2(dir, DB_FILENAME);
|
|
23337
25092
|
let db = openWithPragmas(file2);
|
|
23338
25093
|
if (isForeignSqliteLineage(db)) {
|
|
23339
25094
|
db.close();
|
|
@@ -23352,6 +25107,7 @@ function openLocalDatabase(dir) {
|
|
|
23352
25107
|
const scanLedger = new SqliteScanLedgerRepository(db);
|
|
23353
25108
|
const exceptions = new SqliteExceptionsRepository(db);
|
|
23354
25109
|
const resolutions = new SqliteResolutionsRepository(db);
|
|
25110
|
+
const ruleProbeCache = new SqliteRuleProbeCacheRepository(db);
|
|
23355
25111
|
const security = new SqliteSecurityRepository(db);
|
|
23356
25112
|
const detections = new SqliteDetectionsRepository(db);
|
|
23357
25113
|
const shares = new SqliteSharesRepository(db);
|
|
@@ -23410,7 +25166,7 @@ function openLocalDatabase(dir) {
|
|
|
23410
25166
|
const definitionId = definitionIds.get(`${finding.ruleId}@${finding.version}`);
|
|
23411
25167
|
if (!definitionId) continue;
|
|
23412
25168
|
inspectionFindings.insertFinding({
|
|
23413
|
-
id:
|
|
25169
|
+
id: randomUUID8(),
|
|
23414
25170
|
auditEventId: record2.scanEvent.id,
|
|
23415
25171
|
inspectionDefinitionId: definitionId,
|
|
23416
25172
|
span: finding.span,
|
|
@@ -23443,8 +25199,8 @@ function openLocalDatabase(dir) {
|
|
|
23443
25199
|
}
|
|
23444
25200
|
function reconcileWorktreeProjects(canonicalId, headRoot, worktreeRoot) {
|
|
23445
25201
|
try {
|
|
23446
|
-
const headPosix = headRoot.split(
|
|
23447
|
-
const worktreePosix = worktreeRoot.split(
|
|
25202
|
+
const headPosix = headRoot.split(sep).join("/");
|
|
25203
|
+
const worktreePosix = worktreeRoot.split(sep).join("/");
|
|
23448
25204
|
const exactArm = worktreeRoot === headRoot ? "" : "url IN (:worktreePosix, :worktreeWin) OR ";
|
|
23449
25205
|
const stale = db.prepare(
|
|
23450
25206
|
`SELECT id FROM source_project
|
|
@@ -23489,6 +25245,7 @@ function openLocalDatabase(dir) {
|
|
|
23489
25245
|
scanLedger,
|
|
23490
25246
|
exceptions,
|
|
23491
25247
|
resolutions,
|
|
25248
|
+
ruleProbeCache,
|
|
23492
25249
|
security,
|
|
23493
25250
|
detections,
|
|
23494
25251
|
shares,
|
|
@@ -23520,12 +25277,12 @@ function openLocalDatabase(dir) {
|
|
|
23520
25277
|
|
|
23521
25278
|
// ../packages/persistence/src/fingerprint.ts
|
|
23522
25279
|
import { createHmac, randomBytes } from "crypto";
|
|
23523
|
-
import { chmodSync as chmodSync2, readFileSync as
|
|
23524
|
-
import { join as
|
|
25280
|
+
import { chmodSync as chmodSync2, readFileSync as readFileSync2, renameSync as renameSync2, writeFileSync } from "fs";
|
|
25281
|
+
import { join as join3 } from "path";
|
|
23525
25282
|
var KEY_FILENAME = "exception.key";
|
|
23526
25283
|
var KEY_MATERIAL_BYTES = 32;
|
|
23527
25284
|
function keyFilePath(dataDir2) {
|
|
23528
|
-
return
|
|
25285
|
+
return join3(dataDir2, KEY_FILENAME);
|
|
23529
25286
|
}
|
|
23530
25287
|
function parseKeyFile(raw) {
|
|
23531
25288
|
const parsed = JSON.parse(raw);
|
|
@@ -23562,7 +25319,7 @@ function writeKeyFile(dataDir2, key) {
|
|
|
23562
25319
|
function readFingerprintKey(dataDir2) {
|
|
23563
25320
|
let raw;
|
|
23564
25321
|
try {
|
|
23565
|
-
raw =
|
|
25322
|
+
raw = readFileSync2(keyFilePath(dataDir2), "utf8");
|
|
23566
25323
|
} catch (err) {
|
|
23567
25324
|
if (err.code === "ENOENT") return null;
|
|
23568
25325
|
throw err instanceof Error ? err : new Error(String(err));
|
|
@@ -23595,30 +25352,48 @@ function fingerprintValue(key, raw) {
|
|
|
23595
25352
|
import { chmodSync as chmodSync3, mkdirSync as mkdirSync2, renameSync as renameSync3 } from "fs";
|
|
23596
25353
|
import { chmod, mkdir } from "fs/promises";
|
|
23597
25354
|
import { homedir as homedir3 } from "os";
|
|
23598
|
-
import { join as
|
|
25355
|
+
import { join as join4 } from "path";
|
|
23599
25356
|
function defaultDataDir() {
|
|
23600
|
-
return
|
|
25357
|
+
return join4(homedir3(), ".aka");
|
|
23601
25358
|
}
|
|
23602
25359
|
function settingsDir(base = defaultDataDir()) {
|
|
23603
|
-
return
|
|
25360
|
+
return join4(base, "settings");
|
|
23604
25361
|
}
|
|
23605
25362
|
function dataDir(base = defaultDataDir()) {
|
|
23606
|
-
return
|
|
25363
|
+
return join4(base, "data");
|
|
23607
25364
|
}
|
|
23608
25365
|
function dbPath(base = defaultDataDir()) {
|
|
23609
|
-
return
|
|
25366
|
+
return join4(dataDir(base), "aka.db");
|
|
23610
25367
|
}
|
|
23611
25368
|
function ensureLayoutDirSync(dir = defaultDataDir()) {
|
|
23612
25369
|
ensureDataDirSync(dir);
|
|
23613
25370
|
}
|
|
23614
25371
|
|
|
23615
25372
|
// ../packages/persistence/src/settings.ts
|
|
23616
|
-
import { readFileSync as
|
|
23617
|
-
import { join as
|
|
25373
|
+
import { readFileSync as readFileSync3, renameSync as renameSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
25374
|
+
import { join as join5 } from "path";
|
|
25375
|
+
function readWorkspaceSettings(base = defaultDataDir()) {
|
|
25376
|
+
const record2 = readJson(join5(settingsDir(base), "settings.json"));
|
|
25377
|
+
if (!record2) return defaultWorkspaceSettings();
|
|
25378
|
+
try {
|
|
25379
|
+
return WorkspaceSettings.parse(record2);
|
|
25380
|
+
} catch {
|
|
25381
|
+
return defaultWorkspaceSettings();
|
|
25382
|
+
}
|
|
25383
|
+
}
|
|
25384
|
+
function readJson(file2) {
|
|
25385
|
+
let text;
|
|
25386
|
+
try {
|
|
25387
|
+
text = readFileSync3(file2, "utf8");
|
|
25388
|
+
} catch {
|
|
25389
|
+
return null;
|
|
25390
|
+
}
|
|
25391
|
+
return parseJsonObject(text) ?? null;
|
|
25392
|
+
}
|
|
23618
25393
|
|
|
23619
25394
|
// ../packages/persistence/src/warn-era-cap.ts
|
|
23620
25395
|
import { existsSync as existsSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
23621
|
-
import { join as
|
|
25396
|
+
import { join as join6 } from "path";
|
|
23622
25397
|
|
|
23623
25398
|
// ../packages/plugin-sdk/src/provider-env.ts
|
|
23624
25399
|
var booleanish = external_exports.string().optional().transform((v) => {
|
|
@@ -23639,9 +25414,9 @@ var providerEnvShape = {
|
|
|
23639
25414
|
var ProviderEnvSchema = external_exports.object(providerEnvShape);
|
|
23640
25415
|
|
|
23641
25416
|
// ../packages/plugin-sdk/src/config-inventory.ts
|
|
23642
|
-
import { readdirSync
|
|
25417
|
+
import { readdirSync, readFileSync as readFileSync5, realpathSync, statSync as statSync2 } from "fs";
|
|
23643
25418
|
import { homedir as homedir4 } from "os";
|
|
23644
|
-
import { basename as basename2, join as
|
|
25419
|
+
import { basename as basename2, join as join8 } from "path";
|
|
23645
25420
|
|
|
23646
25421
|
// ../rules/code-flaws/auth-jwt-no-verify.json
|
|
23647
25422
|
var auth_jwt_no_verify_default = {
|
|
@@ -25676,8 +27451,8 @@ function bundledDetections() {
|
|
|
25676
27451
|
}
|
|
25677
27452
|
|
|
25678
27453
|
// ../packages/plugin-sdk/src/repo.ts
|
|
25679
|
-
import { existsSync as existsSync4, readFileSync as
|
|
25680
|
-
import { basename, dirname as dirname2, isAbsolute, join as
|
|
27454
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, statSync } from "fs";
|
|
27455
|
+
import { basename, dirname as dirname2, isAbsolute, join as join7, sep as sep2 } from "path";
|
|
25681
27456
|
function resolveRepoIdentity(cwd) {
|
|
25682
27457
|
try {
|
|
25683
27458
|
const root = findGitRoot(cwd);
|
|
@@ -25689,7 +27464,7 @@ function resolveRepoIdentity(cwd) {
|
|
|
25689
27464
|
// The path fallback is normalized to posix separators (a no-op outside
|
|
25690
27465
|
// win32) so the persistence layer's `/`-separated checkout-path patterns
|
|
25691
27466
|
// (the ghost sweep + the read-side worktree filter) match it as written.
|
|
25692
|
-
url: url2 ?? headRoot.split(
|
|
27467
|
+
url: url2 ?? headRoot.split(sep2).join("/"),
|
|
25693
27468
|
name: (url2 ? slugFromUrl(url2) : void 0) ?? basename(headRoot)
|
|
25694
27469
|
};
|
|
25695
27470
|
} catch {
|
|
@@ -25715,36 +27490,36 @@ function resolveHeadRoot(cwd) {
|
|
|
25715
27490
|
function findGitRoot(start) {
|
|
25716
27491
|
let dir = start;
|
|
25717
27492
|
for (; ; ) {
|
|
25718
|
-
if (existsSync4(
|
|
27493
|
+
if (existsSync4(join7(dir, ".git"))) return dir;
|
|
25719
27494
|
const parent = dirname2(dir);
|
|
25720
27495
|
if (parent === dir) return void 0;
|
|
25721
27496
|
dir = parent;
|
|
25722
27497
|
}
|
|
25723
27498
|
}
|
|
25724
27499
|
function resolveGitContext(root) {
|
|
25725
|
-
const dotGit =
|
|
27500
|
+
const dotGit = join7(root, ".git");
|
|
25726
27501
|
try {
|
|
25727
|
-
if (
|
|
25728
|
-
return { configPath:
|
|
27502
|
+
if (statSync(dotGit).isDirectory()) {
|
|
27503
|
+
return { configPath: join7(dotGit, "config"), headRoot: root };
|
|
25729
27504
|
}
|
|
25730
27505
|
} catch {
|
|
25731
27506
|
return void 0;
|
|
25732
27507
|
}
|
|
25733
27508
|
const target = /^gitdir:\s*(.+?)\s*$/m.exec(safeRead(dotGit) ?? "")?.[1];
|
|
25734
27509
|
if (!target) return void 0;
|
|
25735
|
-
const gitdir = isAbsolute(target) ? target :
|
|
25736
|
-
if (existsSync4(
|
|
25737
|
-
return { configPath:
|
|
27510
|
+
const gitdir = isAbsolute(target) ? target : join7(root, target);
|
|
27511
|
+
if (existsSync4(join7(gitdir, "config"))) {
|
|
27512
|
+
return { configPath: join7(gitdir, "config"), headRoot: root };
|
|
25738
27513
|
}
|
|
25739
|
-
const commonRaw = safeRead(
|
|
27514
|
+
const commonRaw = safeRead(join7(gitdir, "commondir"))?.trim();
|
|
25740
27515
|
if (!commonRaw) return void 0;
|
|
25741
|
-
const commonGitDir = isAbsolute(commonRaw) ? commonRaw :
|
|
27516
|
+
const commonGitDir = isAbsolute(commonRaw) ? commonRaw : join7(gitdir, commonRaw);
|
|
25742
27517
|
const headRoot = basename(commonGitDir) === ".git" ? dirname2(commonGitDir) : root;
|
|
25743
|
-
return { configPath:
|
|
27518
|
+
return { configPath: join7(commonGitDir, "config"), headRoot };
|
|
25744
27519
|
}
|
|
25745
27520
|
function safeRead(path) {
|
|
25746
27521
|
try {
|
|
25747
|
-
return
|
|
27522
|
+
return readFileSync4(path, "utf8");
|
|
25748
27523
|
} catch {
|
|
25749
27524
|
return void 0;
|
|
25750
27525
|
}
|
|
@@ -25782,23 +27557,57 @@ function slugFromUrl(url2) {
|
|
|
25782
27557
|
}
|
|
25783
27558
|
|
|
25784
27559
|
// ../packages/plugin-sdk/src/events.ts
|
|
25785
|
-
import { createHash as
|
|
27560
|
+
import { createHash as createHash3, randomUUID as randomUUID9 } from "crypto";
|
|
25786
27561
|
|
|
25787
27562
|
// ../packages/plugin-sdk/src/finding-key.ts
|
|
25788
|
-
import { createHash as
|
|
27563
|
+
import { createHash as createHash4 } from "crypto";
|
|
25789
27564
|
|
|
25790
27565
|
// ../packages/plugin-sdk/src/inventory-resolver.ts
|
|
25791
27566
|
import { arch, hostname as hostname3, platform, release } from "os";
|
|
25792
27567
|
|
|
25793
27568
|
// ../packages/plugin-sdk/src/nudge.ts
|
|
25794
|
-
import { mkdirSync as mkdirSync3, readFileSync as
|
|
25795
|
-
import { join as
|
|
27569
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
|
|
27570
|
+
import { join as join9 } from "path";
|
|
27571
|
+
|
|
27572
|
+
// ../packages/plugin-sdk/src/paths.ts
|
|
27573
|
+
import { readdirSync as readdirSync2, realpathSync as realpathSync2 } from "fs";
|
|
27574
|
+
import { basename as basename3, dirname as dirname3, sep as sep3 } from "path";
|
|
27575
|
+
function toPosix(path) {
|
|
27576
|
+
return path.split(sep3).join("/");
|
|
27577
|
+
}
|
|
27578
|
+
var MAX_PROJECT_ROOT_LEVELS = 40;
|
|
27579
|
+
function findProjectRoot(startDir, recognizeMarker) {
|
|
27580
|
+
let dir = startDir;
|
|
27581
|
+
let root = null;
|
|
27582
|
+
for (let level = 0; level < MAX_PROJECT_ROOT_LEVELS; level += 1) {
|
|
27583
|
+
if (directoryHasMarker(dir, recognizeMarker)) root = dir;
|
|
27584
|
+
const parent = dirname3(dir);
|
|
27585
|
+
if (parent === dir) break;
|
|
27586
|
+
dir = parent;
|
|
27587
|
+
}
|
|
27588
|
+
return root ?? startDir;
|
|
27589
|
+
}
|
|
27590
|
+
function directoryHasMarker(dir, recognizeMarker) {
|
|
27591
|
+
try {
|
|
27592
|
+
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
27593
|
+
if (entry.isFile() && recognizeMarker(entry.name) != null) return true;
|
|
27594
|
+
}
|
|
27595
|
+
} catch {
|
|
27596
|
+
return false;
|
|
27597
|
+
}
|
|
27598
|
+
return false;
|
|
27599
|
+
}
|
|
27600
|
+
function resolveNonGitProject(startDir, recognizeMarker) {
|
|
27601
|
+
const projectRoot = findProjectRoot(startDir, recognizeMarker);
|
|
27602
|
+
const realRoot = realpathSync2(projectRoot);
|
|
27603
|
+
return { root: projectRoot, projectKey: `path:${realRoot}`, project: basename3(realRoot) };
|
|
27604
|
+
}
|
|
25796
27605
|
|
|
25797
27606
|
// ../packages/plugin-sdk/src/project-files.ts
|
|
25798
|
-
var
|
|
25799
|
-
import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as
|
|
25800
|
-
import { basename as
|
|
25801
|
-
var
|
|
27607
|
+
var import_ignore = __toESM(require_ignore(), 1);
|
|
27608
|
+
import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync7 } from "fs";
|
|
27609
|
+
import { basename as basename4, join as join10, relative, sep as sep4 } from "path";
|
|
27610
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
25802
27611
|
".git",
|
|
25803
27612
|
"node_modules",
|
|
25804
27613
|
".pnpm-store",
|
|
@@ -25815,10 +27624,10 @@ var SKIP_DIRS2 = /* @__PURE__ */ new Set([
|
|
|
25815
27624
|
".cache"
|
|
25816
27625
|
]);
|
|
25817
27626
|
var MAX_FILES = 2e4;
|
|
25818
|
-
function
|
|
27627
|
+
function readIgnoreLayer(dir) {
|
|
25819
27628
|
try {
|
|
25820
|
-
const content =
|
|
25821
|
-
return { base: dir, matcher: (0,
|
|
27629
|
+
const content = readFileSync7(join10(dir, ".gitignore"), "utf8");
|
|
27630
|
+
return { base: dir, matcher: (0, import_ignore.default)().add(content) };
|
|
25822
27631
|
} catch {
|
|
25823
27632
|
return void 0;
|
|
25824
27633
|
}
|
|
@@ -25826,7 +27635,7 @@ function readIgnoreLayer2(dir) {
|
|
|
25826
27635
|
function isIgnored(layers, absPath, isDir) {
|
|
25827
27636
|
let ignored = false;
|
|
25828
27637
|
for (const layer of layers) {
|
|
25829
|
-
const rel =
|
|
27638
|
+
const rel = relative(layer.base, absPath).split(sep4).join("/") + (isDir ? "/" : "");
|
|
25830
27639
|
const verdict = layer.matcher.test(rel);
|
|
25831
27640
|
if (verdict.ignored) ignored = true;
|
|
25832
27641
|
else if (verdict.unignored) ignored = false;
|
|
@@ -25892,13 +27701,13 @@ function resolveProjectFiles(cwd) {
|
|
|
25892
27701
|
walk.lostSubtree = true;
|
|
25893
27702
|
return false;
|
|
25894
27703
|
}
|
|
25895
|
-
const layer =
|
|
27704
|
+
const layer = readIgnoreLayer(dir);
|
|
25896
27705
|
const dirLayers = layer ? [...layers, layer] : layers;
|
|
25897
27706
|
for (const entry of dirents) {
|
|
25898
|
-
const fullPath =
|
|
27707
|
+
const fullPath = join10(dir, entry.name);
|
|
25899
27708
|
if (entry.isDirectory()) {
|
|
25900
|
-
if (
|
|
25901
|
-
if (existsSync5(
|
|
27709
|
+
if (SKIP_DIRS.has(entry.name) || isIgnored(dirLayers, fullPath, true)) continue;
|
|
27710
|
+
if (existsSync5(join10(fullPath, ".git"))) continue;
|
|
25902
27711
|
if (visit3(fullPath, dirLayers)) return true;
|
|
25903
27712
|
continue;
|
|
25904
27713
|
}
|
|
@@ -25906,10 +27715,10 @@ function resolveProjectFiles(cwd) {
|
|
|
25906
27715
|
if (entry.name === ".git") continue;
|
|
25907
27716
|
if (isIgnored(dirLayers, fullPath, false)) continue;
|
|
25908
27717
|
if (files.length >= MAX_FILES) return true;
|
|
25909
|
-
const relPath =
|
|
27718
|
+
const relPath = relative(root, fullPath).split(sep4).join("/");
|
|
25910
27719
|
files.push({
|
|
25911
27720
|
path: relPath,
|
|
25912
|
-
name:
|
|
27721
|
+
name: basename4(entry.name),
|
|
25913
27722
|
origin: classifyOrigin(relPath, entry.name),
|
|
25914
27723
|
defaultAccess: "approved"
|
|
25915
27724
|
});
|
|
@@ -25933,20 +27742,206 @@ function resolveProjectFiles(cwd) {
|
|
|
25933
27742
|
}
|
|
25934
27743
|
|
|
25935
27744
|
// ../packages/plugin-sdk/src/runtime.ts
|
|
25936
|
-
import { randomUUID as
|
|
27745
|
+
import { randomUUID as randomUUID10 } from "crypto";
|
|
25937
27746
|
|
|
25938
27747
|
// ../packages/plugin-sdk/src/suppressions.ts
|
|
25939
27748
|
var THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
25940
27749
|
|
|
25941
27750
|
// ../packages/plugin-sdk/src/throttle.ts
|
|
25942
|
-
import { mkdirSync as mkdirSync4, statSync as
|
|
25943
|
-
import { join as
|
|
27751
|
+
import { mkdirSync as mkdirSync4, statSync as statSync3, writeFileSync as writeFileSync5 } from "fs";
|
|
27752
|
+
import { join as join11 } from "path";
|
|
27753
|
+
|
|
27754
|
+
// ../packages/local-ops/src/egress-record.ts
|
|
27755
|
+
function recordProjectEgress(db, target, egress, base = defaultDataDir()) {
|
|
27756
|
+
try {
|
|
27757
|
+
if (!readWorkspaceSettings(base).dataSharesInPlace) return null;
|
|
27758
|
+
const abs = resolve(target);
|
|
27759
|
+
const targetDir = statSync4(abs).isDirectory() ? abs : dirname4(abs);
|
|
27760
|
+
const identity = resolveRepoIdentity(abs);
|
|
27761
|
+
const worktreeRoot = resolveWorktreeRoot(abs);
|
|
27762
|
+
let root;
|
|
27763
|
+
let projectKey;
|
|
27764
|
+
let project;
|
|
27765
|
+
let projectId;
|
|
27766
|
+
if (identity && worktreeRoot) {
|
|
27767
|
+
root = worktreeRoot;
|
|
27768
|
+
projectKey = `git:${identity.url}`;
|
|
27769
|
+
project = identity.name;
|
|
27770
|
+
projectId = db.sourceProject.upsert({
|
|
27771
|
+
url: identity.url,
|
|
27772
|
+
name: identity.name,
|
|
27773
|
+
attributes: {}
|
|
27774
|
+
});
|
|
27775
|
+
} else {
|
|
27776
|
+
({ root, projectKey, project } = resolveNonGitProject(targetDir, manifestKindOf));
|
|
27777
|
+
projectId = null;
|
|
27778
|
+
}
|
|
27779
|
+
const files = [];
|
|
27780
|
+
for (const hit of egress.files) {
|
|
27781
|
+
const file2 = toPosix(relative2(root, hit.file));
|
|
27782
|
+
if (file2 === "" || file2.startsWith("../")) continue;
|
|
27783
|
+
if (file2.startsWith(".") || file2.includes("/.")) continue;
|
|
27784
|
+
files.push({ ...hit, file: file2, vendored: isVendoredPath(file2) });
|
|
27785
|
+
}
|
|
27786
|
+
const summary = db.shares.recordProjectEgress({
|
|
27787
|
+
projectKey,
|
|
27788
|
+
project,
|
|
27789
|
+
projectId,
|
|
27790
|
+
reconcile: { mode: "walk", walkedPrefix: toPosix(relative2(root, abs)) },
|
|
27791
|
+
hits: resolveEgress(files)
|
|
27792
|
+
});
|
|
27793
|
+
return { ...summary, project };
|
|
27794
|
+
} catch {
|
|
27795
|
+
return null;
|
|
27796
|
+
}
|
|
27797
|
+
}
|
|
27798
|
+
|
|
27799
|
+
// ../packages/local-ops/src/fs-scan.ts
|
|
27800
|
+
import { createHash as createHash5, randomUUID as randomUUID11 } from "crypto";
|
|
27801
|
+
import { readdirSync as readdirSync4, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
|
|
27802
|
+
import { basename as basename5, extname, join as join12, relative as relative3 } from "path";
|
|
27803
|
+
var import_ignore2 = __toESM(require_ignore(), 1);
|
|
27804
|
+
var AKAIGNORE_FILENAME = ".akaignore";
|
|
27805
|
+
var SKIP_DIRS2 = /* @__PURE__ */ new Set([
|
|
27806
|
+
"node_modules",
|
|
27807
|
+
".git",
|
|
27808
|
+
"dist",
|
|
27809
|
+
".next",
|
|
27810
|
+
"coverage",
|
|
27811
|
+
".turbo",
|
|
27812
|
+
"build",
|
|
27813
|
+
"out"
|
|
27814
|
+
]);
|
|
27815
|
+
var MAX_BYTES = 1e6;
|
|
27816
|
+
function readIgnoreLayer2(dir, filename) {
|
|
27817
|
+
try {
|
|
27818
|
+
const content = readFileSync8(join12(dir, filename), "utf8");
|
|
27819
|
+
return { base: dir, matcher: (0, import_ignore2.default)().add(content) };
|
|
27820
|
+
} catch {
|
|
27821
|
+
return void 0;
|
|
27822
|
+
}
|
|
27823
|
+
}
|
|
27824
|
+
function evaluate(layers, absPath, isDir) {
|
|
27825
|
+
let state = "unmatched";
|
|
27826
|
+
for (const layer of layers) {
|
|
27827
|
+
const rel = toPosix(relative3(layer.base, absPath)) + (isDir ? "/" : "");
|
|
27828
|
+
const verdict = layer.matcher.test(rel);
|
|
27829
|
+
if (verdict.ignored) state = "ignored";
|
|
27830
|
+
else if (verdict.unignored) state = "unignored";
|
|
27831
|
+
}
|
|
27832
|
+
return state;
|
|
27833
|
+
}
|
|
27834
|
+
function* collectFiles(target) {
|
|
27835
|
+
let st;
|
|
27836
|
+
try {
|
|
27837
|
+
st = statSync5(target);
|
|
27838
|
+
} catch {
|
|
27839
|
+
return;
|
|
27840
|
+
}
|
|
27841
|
+
if (st.isFile()) {
|
|
27842
|
+
if (st.size <= MAX_BYTES) yield { path: target, gitignored: false };
|
|
27843
|
+
return;
|
|
27844
|
+
}
|
|
27845
|
+
if (!st.isDirectory()) return;
|
|
27846
|
+
yield* visit(target, [], [], false);
|
|
27847
|
+
}
|
|
27848
|
+
function* visit(dir, markLayers, skipLayers, inIgnoredDir) {
|
|
27849
|
+
const markLayer = inIgnoredDir ? void 0 : readIgnoreLayer2(dir, ".gitignore");
|
|
27850
|
+
const dirMarkLayers = markLayer ? [...markLayers, markLayer] : markLayers;
|
|
27851
|
+
const skipLayer = readIgnoreLayer2(dir, AKAIGNORE_FILENAME);
|
|
27852
|
+
const dirSkipLayers = skipLayer ? [...skipLayers, skipLayer] : skipLayers;
|
|
27853
|
+
for (const entry of readdirSync4(dir, { withFileTypes: true })) {
|
|
27854
|
+
const path = join12(dir, entry.name);
|
|
27855
|
+
if (entry.isDirectory()) {
|
|
27856
|
+
const skipState = evaluate(dirSkipLayers, path, true);
|
|
27857
|
+
if (skipState !== "unignored" && (SKIP_DIRS2.has(entry.name) || entry.name.startsWith(".") || skipState === "ignored")) {
|
|
27858
|
+
continue;
|
|
27859
|
+
}
|
|
27860
|
+
const dirIgnored = inIgnoredDir || evaluate(dirMarkLayers, path, true) === "ignored";
|
|
27861
|
+
yield* visit(path, dirMarkLayers, dirSkipLayers, dirIgnored);
|
|
27862
|
+
} else if (entry.isFile()) {
|
|
27863
|
+
if (evaluate(dirSkipLayers, path, false) === "ignored") continue;
|
|
27864
|
+
let size;
|
|
27865
|
+
try {
|
|
27866
|
+
size = statSync5(path).size;
|
|
27867
|
+
} catch {
|
|
27868
|
+
continue;
|
|
27869
|
+
}
|
|
27870
|
+
if (size > MAX_BYTES) continue;
|
|
27871
|
+
yield {
|
|
27872
|
+
path,
|
|
27873
|
+
gitignored: inIgnoredDir || evaluate(dirMarkLayers, path, false) === "ignored"
|
|
27874
|
+
};
|
|
27875
|
+
}
|
|
27876
|
+
}
|
|
27877
|
+
}
|
|
27878
|
+
function extractFileEgress(file2, text) {
|
|
27879
|
+
if (text.includes("\0")) return null;
|
|
27880
|
+
const name = basename5(file2);
|
|
27881
|
+
if (LOCKFILE_BASENAMES.has(name)) return null;
|
|
27882
|
+
const kind = manifestKindOf(name);
|
|
27883
|
+
const sdkHits = kind === null ? [] : extractManifestSdks(text, kind);
|
|
27884
|
+
const endpoints = kind === null && EGRESS_CODE_EXTENSIONS.has(extname(file2)) ? extractEgress(text) : [];
|
|
27885
|
+
if (endpoints.length === 0 && sdkHits.length === 0) return null;
|
|
27886
|
+
return { file: file2, vendored: isVendoredPath(toPosix(file2)), endpoints, sdkHits };
|
|
27887
|
+
}
|
|
27888
|
+
function scanPathIntoStore(db, target, opts = {}) {
|
|
27889
|
+
let scanned = 0;
|
|
27890
|
+
let findingCount = 0;
|
|
27891
|
+
const files = [];
|
|
27892
|
+
const egressFiles = [];
|
|
27893
|
+
for (const { path: file2, gitignored } of collectFiles(target)) {
|
|
27894
|
+
let text;
|
|
27895
|
+
try {
|
|
27896
|
+
text = readFileSync8(file2, "utf8");
|
|
27897
|
+
} catch {
|
|
27898
|
+
continue;
|
|
27899
|
+
}
|
|
27900
|
+
scanned++;
|
|
27901
|
+
const fileEgress = extractFileEgress(file2, text);
|
|
27902
|
+
if (fileEgress) egressFiles.push(fileEgress);
|
|
27903
|
+
const matches = scan(text, opts.rules);
|
|
27904
|
+
if (matches.length === 0) continue;
|
|
27905
|
+
const eventId = randomUUID11();
|
|
27906
|
+
const metadata = { filePath: file2 };
|
|
27907
|
+
if (gitignored) metadata.gitignored = true;
|
|
27908
|
+
const event = {
|
|
27909
|
+
id: eventId,
|
|
27910
|
+
sourceTool: opts.sourceTool ?? "cli",
|
|
27911
|
+
kind: "code_change",
|
|
27912
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27913
|
+
contentHash: createHash5("sha256").update(text).digest("hex"),
|
|
27914
|
+
content: redact(text, matches),
|
|
27915
|
+
// store the REDACTED file, never the raw secret
|
|
27916
|
+
metadata
|
|
27917
|
+
};
|
|
27918
|
+
const findings = matches.map((m) => ({
|
|
27919
|
+
id: randomUUID11(),
|
|
27920
|
+
eventId,
|
|
27921
|
+
ruleId: m.ruleId,
|
|
27922
|
+
category: m.category,
|
|
27923
|
+
severity: m.severity,
|
|
27924
|
+
span: m.span,
|
|
27925
|
+
maskedMatch: maskMatch(m.rawMatch),
|
|
27926
|
+
// Per-pack action (monitor-by-default) when the installed snapshot supplies
|
|
27927
|
+
// one, else the per-category fallback — mirrors the live path's resolveAction.
|
|
27928
|
+
actionTaken: opts.ruleActions?.get(m.ruleId) ?? DEFAULT_ACTIONS[m.category],
|
|
27929
|
+
confidence: m.confidence
|
|
27930
|
+
}));
|
|
27931
|
+
db.recordCapture(event, findings);
|
|
27932
|
+
files.push({ path: file2, gitignored, findings });
|
|
27933
|
+
findingCount += findings.length;
|
|
27934
|
+
}
|
|
27935
|
+
return { scanned, findings: findingCount, files, egress: { files: egressFiles } };
|
|
27936
|
+
}
|
|
25944
27937
|
|
|
25945
27938
|
// ../packages/local-ops/src/project-inventory.ts
|
|
27939
|
+
import { statSync as statSync6 } from "fs";
|
|
27940
|
+
import { resolve as resolve2 } from "path";
|
|
25946
27941
|
function recordProjectInventory(db, target) {
|
|
25947
27942
|
try {
|
|
25948
|
-
const abs =
|
|
25949
|
-
|
|
27943
|
+
const abs = resolve2(target);
|
|
27944
|
+
statSync6(abs);
|
|
25950
27945
|
const identity = resolveRepoIdentity(abs);
|
|
25951
27946
|
if (!identity) return null;
|
|
25952
27947
|
const sourceProjectId2 = db.sourceProject.upsert({
|
|
@@ -26366,7 +28361,7 @@ import { spawn as spawn3 } from "child_process";
|
|
|
26366
28361
|
import { existsSync as existsSync8 } from "fs";
|
|
26367
28362
|
import { createRequire as createRequire2 } from "module";
|
|
26368
28363
|
import { createServer } from "net";
|
|
26369
|
-
import { dirname as
|
|
28364
|
+
import { dirname as dirname5, join as join14 } from "path";
|
|
26370
28365
|
import { fileURLToPath as fileURLToPath2, pathToFileURL } from "url";
|
|
26371
28366
|
import { parseArgs as parseArgs2 } from "util";
|
|
26372
28367
|
|
|
@@ -26384,19 +28379,19 @@ function openUrl(url2) {
|
|
|
26384
28379
|
|
|
26385
28380
|
// src/commands/dashboard.ts
|
|
26386
28381
|
var require3 = createRequire2(import.meta.url);
|
|
26387
|
-
var here =
|
|
28382
|
+
var here = dirname5(fileURLToPath2(import.meta.url));
|
|
26388
28383
|
function pkgBase() {
|
|
26389
|
-
return isSea() ?
|
|
28384
|
+
return isSea() ? dirname5(process.execPath) : join14(here, "..");
|
|
26390
28385
|
}
|
|
26391
28386
|
function isPortFree(port) {
|
|
26392
|
-
return new Promise((
|
|
28387
|
+
return new Promise((resolve3) => {
|
|
26393
28388
|
const probe = createServer();
|
|
26394
28389
|
probe.once("error", () => {
|
|
26395
|
-
|
|
28390
|
+
resolve3(false);
|
|
26396
28391
|
});
|
|
26397
28392
|
probe.once("listening", () => {
|
|
26398
28393
|
probe.close(() => {
|
|
26399
|
-
|
|
28394
|
+
resolve3(true);
|
|
26400
28395
|
});
|
|
26401
28396
|
});
|
|
26402
28397
|
probe.listen(port, "127.0.0.1");
|
|
@@ -26447,7 +28442,7 @@ async function runDashboard(argv) {
|
|
|
26447
28442
|
launchStandalone(bundledServer, port, shouldOpen, url2);
|
|
26448
28443
|
return;
|
|
26449
28444
|
}
|
|
26450
|
-
const webUiDir =
|
|
28445
|
+
const webUiDir = dirname5(require3.resolve("@akasecurity/web-ui/package.json"));
|
|
26451
28446
|
const nextBin = require3.resolve("next/dist/bin/next", { paths: [webUiDir] });
|
|
26452
28447
|
const startNext = () => {
|
|
26453
28448
|
const child = spawn3(
|
|
@@ -26484,7 +28479,7 @@ function launchStandalone(serverJs, port, shouldOpen, url2) {
|
|
|
26484
28479
|
return;
|
|
26485
28480
|
}
|
|
26486
28481
|
const child = spawn3(reinvoke.command, reinvoke.args, {
|
|
26487
|
-
cwd:
|
|
28482
|
+
cwd: dirname5(serverJs),
|
|
26488
28483
|
stdio: ["inherit", "pipe", "inherit"],
|
|
26489
28484
|
// Inherit the parent env so the child server sees PATH etc.; PORT/HOSTNAME tell
|
|
26490
28485
|
// Next's standalone server where to bind (its only port input).
|
|
@@ -26703,11 +28698,11 @@ function terminalPrompter() {
|
|
|
26703
28698
|
},
|
|
26704
28699
|
isInteractive: process.stdin.isTTY && process.stdout.isTTY,
|
|
26705
28700
|
ask(question) {
|
|
26706
|
-
return new Promise((
|
|
28701
|
+
return new Promise((resolve3) => {
|
|
26707
28702
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
26708
28703
|
rl.question(question, (answer) => {
|
|
26709
28704
|
rl.close();
|
|
26710
|
-
|
|
28705
|
+
resolve3(answer);
|
|
26711
28706
|
});
|
|
26712
28707
|
});
|
|
26713
28708
|
},
|
|
@@ -26718,7 +28713,7 @@ function terminalPrompter() {
|
|
|
26718
28713
|
// fatal signals: a security tool must never brick the terminal it was
|
|
26719
28714
|
// invoked from.
|
|
26720
28715
|
askHidden(question) {
|
|
26721
|
-
return new Promise((
|
|
28716
|
+
return new Promise((resolve3, reject) => {
|
|
26722
28717
|
const stdin = process.stdin;
|
|
26723
28718
|
process.stdout.write(question);
|
|
26724
28719
|
stdin.setRawMode(true);
|
|
@@ -26741,7 +28736,7 @@ function terminalPrompter() {
|
|
|
26741
28736
|
restore();
|
|
26742
28737
|
process.stdout.write("\n");
|
|
26743
28738
|
if (err) reject(err);
|
|
26744
|
-
else
|
|
28739
|
+
else resolve3(state.value);
|
|
26745
28740
|
};
|
|
26746
28741
|
const onError = (err) => {
|
|
26747
28742
|
finish(err);
|
|
@@ -27601,7 +29596,7 @@ async function confirm(question) {
|
|
|
27601
29596
|
}
|
|
27602
29597
|
|
|
27603
29598
|
// src/commands/scan.ts
|
|
27604
|
-
import { statSync as
|
|
29599
|
+
import { statSync as statSync7 } from "fs";
|
|
27605
29600
|
import { parseArgs as parseArgs7 } from "util";
|
|
27606
29601
|
function severityRank(s) {
|
|
27607
29602
|
return Severity.options.indexOf(s);
|
|
@@ -27611,6 +29606,10 @@ function renderInventoryLine(inv) {
|
|
|
27611
29606
|
const partial2 = inv.truncated ? " (partial walk)" : "";
|
|
27612
29607
|
return `Inventory: ${inv.name} \xB7 ${String(inv.fileCount)} project file(s) recorded${partial2}`;
|
|
27613
29608
|
}
|
|
29609
|
+
function renderEgressLine(egress) {
|
|
29610
|
+
const cap = egress.truncated ? ` (capped at ${String(MAX_EGRESS_CALL_SITES_PER_PROJECT)} call sites)` : "";
|
|
29611
|
+
return `Data shares: ${String(egress.destinations)} destination(s) \xB7 ${String(egress.endpoints)} endpoint(s) \xB7 ${String(egress.callSites)} call site(s)${cap}`;
|
|
29612
|
+
}
|
|
27614
29613
|
function runScan(argv) {
|
|
27615
29614
|
const { values, positionals } = parseArgs7({
|
|
27616
29615
|
args: argv,
|
|
@@ -27644,7 +29643,7 @@ function runScan(argv) {
|
|
|
27644
29643
|
failOn = parsed.data;
|
|
27645
29644
|
}
|
|
27646
29645
|
try {
|
|
27647
|
-
|
|
29646
|
+
statSync7(target);
|
|
27648
29647
|
} catch {
|
|
27649
29648
|
process.stderr.write(`aka scan: no such file or directory: ${target}
|
|
27650
29649
|
`);
|
|
@@ -27655,10 +29654,12 @@ function runScan(argv) {
|
|
|
27655
29654
|
const db = openLocalDatabase(dataDir(home));
|
|
27656
29655
|
let result;
|
|
27657
29656
|
let inventory;
|
|
29657
|
+
let egress;
|
|
27658
29658
|
try {
|
|
27659
29659
|
const { ruleActions } = db.installedPacks.installedRuleset();
|
|
27660
29660
|
result = scanPathIntoStore(db, target, { ruleActions, sourceTool: "cli" });
|
|
27661
29661
|
inventory = recordProjectInventory(db, target);
|
|
29662
|
+
egress = recordProjectEgress(db, target, result.egress, home);
|
|
27662
29663
|
} finally {
|
|
27663
29664
|
db.close();
|
|
27664
29665
|
}
|
|
@@ -27682,8 +29683,18 @@ function runScan(argv) {
|
|
|
27682
29683
|
fileCount: inventory.fileCount,
|
|
27683
29684
|
truncated: inventory.truncated
|
|
27684
29685
|
} : null;
|
|
29686
|
+
const egressJson = egress ? {
|
|
29687
|
+
destinations: egress.destinations,
|
|
29688
|
+
endpoints: egress.endpoints,
|
|
29689
|
+
callSites: egress.callSites,
|
|
29690
|
+
truncated: egress.truncated
|
|
29691
|
+
} : null;
|
|
27685
29692
|
process.stdout.write(
|
|
27686
|
-
`${JSON.stringify(
|
|
29693
|
+
`${JSON.stringify(
|
|
29694
|
+
{ target, scanned: result.scanned, findings, inventory: inventoryJson, egress: egressJson },
|
|
29695
|
+
null,
|
|
29696
|
+
2
|
|
29697
|
+
)}
|
|
27687
29698
|
`
|
|
27688
29699
|
);
|
|
27689
29700
|
} else {
|
|
@@ -27692,6 +29703,8 @@ function runScan(argv) {
|
|
|
27692
29703
|
`
|
|
27693
29704
|
);
|
|
27694
29705
|
if (inventory) process.stdout.write(`${renderInventoryLine(inventory)}
|
|
29706
|
+
`);
|
|
29707
|
+
if (egress) process.stdout.write(`${renderEgressLine(egress)}
|
|
27695
29708
|
`);
|
|
27696
29709
|
}
|
|
27697
29710
|
if (failOn !== void 0) {
|