@@ -145,15 +145,7 @@ def evaluate_heuristic_results(
145
145
what rules were triggered.
146
146
"""
147
147
facts_list : list [str ] = []
148
- triggered_rules = []
149
- # confidence is calculated using the probability of the package being benign, so the negation of the confidence values
150
- # in the problog model. Multiplying these probabilities together on several triggers will further decrease the probability
151
- # of the package being benign. This is then negated after calculation to get the probability of the package being malicious.
152
- # If no rules are triggered, this will simply result in 1.0 - 1.0 = 0.0.
153
- # For example, if a LOW rule and MEDIUM rule are triggered, with confidences 0.4 and 0.7 respectively, this would result in
154
- # the following calculation for confidence in package maliciousness:
155
- # 1 - (1.0 * (1 - 0.4) * (1 - 0.7)) = 0.82
156
- confidence : float = 1.0
148
+ triggered_rules : dict [str , JsonType ] = {}
157
149
158
150
for heuristic , result in heuristic_results .items ():
159
151
if result == HeuristicResult .PASS :
@@ -169,11 +161,11 @@ def evaluate_heuristic_results(
169
161
problog_model = PrologString (problog_code )
170
162
problog_results : dict [Term , float ] = get_evaluatable ().create_from (problog_model ).evaluate ()
171
163
172
- for term , conf in problog_results .items ():
173
- if conf is not None and conf > 0 :
174
- confidence *= 1.0 - conf # decrease the probability of the package being benign
175
- triggered_rules . append ( term .args [ 0 ])
176
- confidence = round ( 1.0 - confidence , 2 ) # 2 decimal places
164
+ confidence = problog_results .pop ( Term ( self . problog_result_access ), 0.0 )
165
+ if confidence > 0 : # a rule was triggered
166
+ for term , conf in problog_results . items ():
167
+ if term .args :
168
+ triggered_rules [ str ( term . args [ 0 ])] = conf
177
169
178
170
return confidence , triggered_rules
179
171
@@ -336,11 +328,21 @@ def run_check(self, ctx: AnalyzeContext) -> CheckResultData:
336
328
AnomalousVersionAnalyzer ,
337
329
]
338
330
331
+ problog_result_access = "result"
332
+
339
333
malware_rules_problog_model = f"""
340
334
% ----- Wrappers ------
341
- % These should be used to logically check for a pass or fail on a heuristic for the rest of the model. They exist since,
342
- % when a heuristic is skipped, it is ommitted from being defined in the ProbLog model, and as such these try_call statements
343
- % are needed to handle referencing an undefined fact.
335
+ % When a heuristic is skipped, it is ommitted from the problog model facts definition. This means that references in this
336
+ % static model must account for when they are not existent. These wrappers perform this function using the inbuilt try_call
337
+ % problog function. It will try to evaluate the provided logic, and return false if it encounters an error, such as the fact
338
+ % not being defined. For example, you are expecting A to pass, so we do:
339
+ %
340
+ % passed(A)
341
+ %
342
+ % If A was 'true', then this will return true, as A did pass. If A was 'false', then this will return false, as A did not pass.
343
+ % If A was not defined, then this will return false, as A did not pass.
344
+ % Please use these wrappers throughout the problog model for logic definitions.
345
+
344
346
passed(H) :- try_call(H).
345
347
failed(H) :- try_call(not H).
346
348
@@ -358,14 +360,14 @@ def run_check(self, ctx: AnalyzeContext) -> CheckResultData:
358
360
% ----- Suspicious Combinations -----
359
361
360
362
% Package released recently with little detail, forcing the setup.py to run.
361
- { Confidence .HIGH .value } ::result(" malware_high_confidence_1" ) :-
363
+ { Confidence .HIGH .value } ::trigger( malware_high_confidence_1) :-
362
364
quickUndetailed, forceSetup, failed({ Heuristics .ONE_RELEASE .value } ).
363
- { Confidence .HIGH .value } ::result(" malware_high_confidence_2" ) :-
365
+ { Confidence .HIGH .value } ::trigger( malware_high_confidence_2) :-
364
366
quickUndetailed, forceSetup, failed({ Heuristics .HIGH_RELEASE_FREQUENCY .value } ).
365
367
366
368
% Package released recently with little detail, with some more refined trust markers introduced: project links,
367
369
% multiple different releases, but there is no source code repository matching it and the setup is suspicious.
368
- { Confidence .HIGH .value } ::result(" malware_high_confidence_3" ) :-
370
+ { Confidence .HIGH .value } ::trigger( malware_high_confidence_3) :-
369
371
failed({ Heuristics .SOURCE_CODE_REPO .value } ),
370
372
failed({ Heuristics .HIGH_RELEASE_FREQUENCY .value } ),
371
373
passed({ Heuristics .UNCHANGED_RELEASE .value } ),
@@ -374,21 +376,31 @@ def run_check(self, ctx: AnalyzeContext) -> CheckResultData:
374
376
375
377
% Package released recently with little detail, with multiple releases as a trust marker, but frequent and with
376
378
% the same code.
377
- { Confidence .MEDIUM .value } ::result(" malware_medium_confidence_1" ) :-
379
+ { Confidence .MEDIUM .value } ::trigger( malware_medium_confidence_1) :-
378
380
quickUndetailed,
379
381
failed({ Heuristics .HIGH_RELEASE_FREQUENCY .value } ),
380
382
failed({ Heuristics .UNCHANGED_RELEASE .value } ),
381
383
passed({ Heuristics .SUSPICIOUS_SETUP .value } ).
382
384
383
385
% Package released recently with little detail and an anomalous version number for a single-release package.
384
- { Confidence .MEDIUM .value } ::result(" malware_medium_confidence_2" ) :-
386
+ { Confidence .MEDIUM .value } ::trigger( malware_medium_confidence_2) :-
385
387
quickUndetailed,
386
388
failed({ Heuristics .ONE_RELEASE .value } ),
387
389
passed({ Heuristics .WHEEL_ABSENCE .value } ),
388
390
failed({ Heuristics .ANOMALOUS_VERSION .value } ).
389
391
390
392
% ----- Evaluation -----
391
- query(result(_)).
393
+
394
+ % Aggregate result
395
+ { problog_result_access } :- trigger(malware_high_confidence_1).
396
+ { problog_result_access } :- trigger(malware_high_confidence_2).
397
+ { problog_result_access } :- trigger(malware_high_confidence_3).
398
+ { problog_result_access } :- trigger(malware_medium_confidence_2).
399
+ { problog_result_access } :- trigger(malware_medium_confidence_1).
400
+ query({ problog_result_access } ).
401
+
402
+ % Explainability
403
+ query(trigger(_)).
392
404
"""
393
405
394
406
0 commit comments