@@ -165,6 +165,69 @@ func TestScanRepeatedScansNoResourceExhaustion(t *testing.T) {
165165 }
166166}
167167
168+ func TestNPMCredentialExfiltrationRule (t * testing.T ) {
169+ ctx := context .Background ()
170+
171+ rfs := []fs.FS {rules .FS , thirdparty .FS }
172+ yrs , err := CachedRules (ctx , rfs )
173+ if err != nil {
174+ t .Fatalf ("rules: %v" , err )
175+ }
176+
177+ cfg := malcontent.Config {
178+ Concurrency : runtime .NumCPU (),
179+ IgnoreSelf : false ,
180+ IncludeDataFiles : false ,
181+ MinFileRisk : 0 ,
182+ MinRisk : 0 ,
183+ Rules : yrs ,
184+ RuleFS : rfs ,
185+ }
186+
187+ tests := []struct {
188+ name string
189+ path string
190+ wantRule bool
191+ }{
192+ {
193+ name : "postinstall credential exfiltration" ,
194+ path : filepath .Join ("testdata" , "npm-token-exfil" , "package.json" ),
195+ wantRule : true ,
196+ },
197+ {
198+ name : "release script token reference" ,
199+ path : filepath .Join ("testdata" , "npm-token-release" , "package.json" ),
200+ wantRule : false ,
201+ },
202+ }
203+
204+ for _ , tt := range tests {
205+ t .Run (tt .name , func (t * testing.T ) {
206+ fr , err := scanSinglePath (ctx , cfg , tt .path , rfs , tt .path , "" , nil )
207+ if err != nil {
208+ t .Fatalf ("scan: %v" , err )
209+ }
210+
211+ gotRule := hasRuleName (fr , "npm_install_credential_exfiltration" )
212+ if gotRule != tt .wantRule {
213+ t .Fatalf ("npm_install_credential_exfiltration match = %t, want %t" , gotRule , tt .wantRule )
214+ }
215+ })
216+ }
217+ }
218+
219+ func hasRuleName (fr * malcontent.FileReport , ruleName string ) bool {
220+ if fr == nil {
221+ return false
222+ }
223+ for _ , b := range fr .Behaviors {
224+ if b .RuleName == ruleName {
225+ return true
226+ }
227+ }
228+ return false
229+ }
230+
168231func TestExitIfHitOrMiss (t * testing.T ) {
169232 t .Parallel ()
170233
0 commit comments