@@ -947,6 +947,12 @@ def protect_cmd(
947947 default = False ,
948948 help = "Query OSV for known malicious package advisories." ,
949949)
950+ @click .option (
951+ "--scan-publisher-changes" ,
952+ is_flag = True ,
953+ default = False ,
954+ help = "Query public npm metadata for recent publisher changes after release dormancy." ,
955+ )
950956@click .option (
951957 "--scan-artifacts" ,
952958 is_flag = True ,
@@ -988,7 +994,7 @@ def protect_cmd(
988994 "--offline" ,
989995 is_flag = True ,
990996 default = False ,
991- help = "Use only cached OSV data for --malware-query." ,
997+ help = "Use cached OSV data for --malware-query; cannot scan publisher changes ." ,
992998)
993999@click .option (
9941000 "--refresh-cache" ,
@@ -1014,6 +1020,7 @@ def vet_cmd(
10141020 private_indexes : tuple [str , ...],
10151021 internal_package_patterns : tuple [str , ...],
10161022 malware_query : bool ,
1023+ scan_publisher_changes : bool ,
10171024 scan_artifacts : bool ,
10181025 scan_workflows : bool ,
10191026 allow_unhashed_downloads : bool ,
@@ -1050,6 +1057,18 @@ def vet_cmd(
10501057 raise click .ClickException (str (exc )) from None
10511058
10521059 inventory = build_inventory (repo_path )
1060+ if offline and scan_publisher_changes :
1061+ raise click .ClickException (
1062+ "--scan-publisher-changes requires npm registry access; remove --offline"
1063+ )
1064+
1065+ publisher_findings = []
1066+ publisher_warnings = []
1067+ if scan_publisher_changes :
1068+ from ca9 .npm_publisher import scan_npm_publisher_changes
1069+
1070+ publisher_findings , publisher_warnings = scan_npm_publisher_changes (inventory .packages )
1071+
10531072 policy_findings = []
10541073 feed_warnings = []
10551074 if package_policy .package_age .enabled :
@@ -1107,7 +1126,7 @@ def vet_cmd(
11071126 raise click .ClickException (str (e )) from None
11081127
11091128 artifact_findings = []
1110- artifact_warnings = [* policy_warnings , * feed_warnings ]
1129+ artifact_warnings = [* policy_warnings , * feed_warnings , * publisher_warnings ]
11111130 artifact_scans = 0
11121131 skipped_artifacts = 0
11131132 workflow_findings = []
@@ -1132,7 +1151,12 @@ def vet_cmd(
11321151 require_known_license = require_known_license ,
11331152 )
11341153 artifact_findings .extend (analyze_license_policy (artifact_result .snapshots , license_policy ))
1135- artifact_warnings = [* policy_warnings , * feed_warnings , * artifact_result .warnings ]
1154+ artifact_warnings = [
1155+ * policy_warnings ,
1156+ * feed_warnings ,
1157+ * publisher_warnings ,
1158+ * artifact_result .warnings ,
1159+ ]
11361160 artifact_scans = artifact_result .scanned_artifacts
11371161 skipped_artifacts = artifact_result .skipped_artifacts
11381162
@@ -1157,7 +1181,12 @@ def vet_cmd(
11571181 inventory ,
11581182 policy = policy ,
11591183 malware_advisories = malware_advisories ,
1160- extra_findings = [* policy_findings , * artifact_findings , * workflow_findings ],
1184+ extra_findings = [
1185+ * policy_findings ,
1186+ * publisher_findings ,
1187+ * artifact_findings ,
1188+ * workflow_findings ,
1189+ ],
11611190 extra_warnings = artifact_warnings ,
11621191 artifact_scans = artifact_scans ,
11631192 skipped_artifacts = skipped_artifacts ,
0 commit comments