muaddib-scanner 2.10.88 → 2.10.91
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/package.json +1 -1
- package/src/ml/model-trees-backup.js +11 -0
- package/src/monitor/daemon.js +37 -8
- package/src/monitor/state.js +55 -9
- package/src/response/playbooks.js +37 -0
- package/src/rules/index.js +58 -0
- package/src/scanner/ast-detectors/constants.js +10 -1
- package/src/scanner/ast-detectors/handle-new-expression.js +21 -0
- package/src/scanner/ast-detectors/handle-post-walk.js +22 -0
- package/src/scanner/ast-detectors/handle-variable-declarator.js +12 -0
- package/src/scanner/package.js +32 -0
- package/src/scoring.js +23 -1
package/package.json
CHANGED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* XGBoost model trees — auto-generated by src/ml/train-xgboost.py
|
|
5
|
+
* 144 trees, 40 features, threshold=0.52
|
|
6
|
+
* CV: P=0.913 R=0.971 F1=0.941
|
|
7
|
+
* Holdout: P=0.999 R=0.929 F1=0.963
|
|
8
|
+
* DO NOT EDIT MANUALLY
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
module.exports = {"version":1,"features":["unpacked_size_bytes","score","global_risk_score","threat_density","file_count_total","package_score","max_single_points","has_tests","count_total","max_file_score","type_lifecycle_script","version_count","points_concentration","author_package_count","type_credential_regex_harvest","distinct_threat_types","count_critical","count_high","count_medium","count_low","type_suspicious_dataflow","type_env_access","type_sensitive_string","type_dangerous_call_eval","type_dangerous_call_exec","type_dangerous_call_function","type_obfuscation_detected","type_high_entropy_string","type_dynamic_require","type_dynamic_import","type_typosquat_detected","type_staged_payload","type_staged_binary_payload","type_network_require","type_sandbox_evasion","type_remote_code_load","type_suspicious_domain","type_prototype_hook","type_intent_credential_exfil","type_crypto_decipher"],"threshold":0.52,"trees":[[{"f":1,"t":2,"y":1,"n":14,"v":0},{"f":5,"t":1,"y":2,"n":11,"v":0},{"f":3,"t":1,"y":3,"n":8,"v":0},{"f":7,"t":1,"y":4,"n":7,"v":0},{"f":1,"t":1,"y":5,"n":6,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.166096},{"f":-1,"t":0,"y":0,"n":0,"v":-0.199006},{"f":-1,"t":0,"y":0,"n":0,"v":-0.19908},{"f":11,"t":1,"y":9,"n":10,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.165379},{"f":-1,"t":0,"y":0,"n":0,"v":-0.196779},{"f":11,"t":1,"y":12,"n":13,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.178149},{"f":-1,"t":0,"y":0,"n":0,"v":-0.178459},{"f":11,"t":1,"y":15,"n":22,"v":0},{"f":3,"t":1,"y":16,"n":21,"v":0},{"f":9,"t":1,"y":17,"n":20,"v":0},{"f":1,"t":4,"y":18,"n":19,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.179951},{"f":-1,"t":0,"y":0,"n":0,"v":0.199021},{"f":-1,"t":0,"y":0,"n":0,"v":-0.199171},{"f":-1,"t":0,"y":0,"n":0,"v":0.199812},{"f":-1,"t":0,"y":0,"n":0,"v":-0.199165}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.023559},{"f":-1,"t":0,"y":0,"n":0,"v":-0.179379},{"f":-1,"t":0,"y":0,"n":0,"v":0.181997},{"f":-1,"t":0,"y":0,"n":0,"v":-0.184263}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.025297},{"f":-1,"t":0,"y":0,"n":0,"v":-0.166025},{"f":-1,"t":0,"y":0,"n":0,"v":0.168281},{"f":-1,"t":0,"y":0,"n":0,"v":-0.170089}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.018951},{"f":-1,"t":0,"y":0,"n":0,"v":-0.155613},{"f":-1,"t":0,"y":0,"n":0,"v":0.157681},{"f":-1,"t":0,"y":0,"n":0,"v":-0.159127}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.019364},{"f":-1,"t":0,"y":0,"n":0,"v":-0.147067},{"f":-1,"t":0,"y":0,"n":0,"v":0.149269},{"f":-1,"t":0,"y":0,"n":0,"v":-0.15043}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.005689},{"f":-1,"t":0,"y":0,"n":0,"v":0.142388},{"f":-1,"t":0,"y":0,"n":0,"v":-0.143374}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":1,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.002889},{"f":-1,"t":0,"y":0,"n":0,"v":0.136737},{"f":-1,"t":0,"y":0,"n":0,"v":-0.137571}],[{"f":1,"t":2,"y":1,"n":10,"v":0},{"f":4,"t":1,"y":2,"n":9,"v":0},{"f":3,"t":1,"y":3,"n":8,"v":0},{"f":9,"t":1,"y":4,"n":7,"v":0},{"f":7,"t":1,"y":5,"n":6,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.091257},{"f":-1,"t":0,"y":0,"n":0,"v":-0.14027},{"f":-1,"t":0,"y":0,"n":0,"v":-0.130949},{"f":-1,"t":0,"y":0,"n":0,"v":0.115779},{"f":-1,"t":0,"y":0,"n":0,"v":-0.132801},{"f":11,"t":1,"y":11,"n":18,"v":0},{"f":3,"t":1,"y":12,"n":17,"v":0},{"f":9,"t":1,"y":13,"n":16,"v":0},{"f":1,"t":4,"y":14,"n":15,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.11795},{"f":-1,"t":0,"y":0,"n":0,"v":0.131006},{"f":-1,"t":0,"y":0,"n":0,"v":-0.131716},{"f":-1,"t":0,"y":0,"n":0,"v":0.131944},{"f":-1,"t":0,"y":0,"n":0,"v":-0.131069}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.021324},{"f":-1,"t":0,"y":0,"n":0,"v":-0.135022},{"f":-1,"t":0,"y":0,"n":0,"v":0.12808},{"f":-1,"t":0,"y":0,"n":0,"v":-0.129409}],[{"f":1,"t":5,"y":1,"n":26,"v":0},{"f":1,"t":2,"y":2,"n":11,"v":0},{"f":4,"t":1,"y":3,"n":10,"v":0},{"f":7,"t":1,"y":4,"n":9,"v":0},{"f":29,"t":1,"y":5,"n":8,"v":0},{"f":2,"t":6,"y":6,"n":7,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.082086},{"f":-1,"t":0,"y":0,"n":0,"v":-0.01764},{"f":-1,"t":0,"y":0,"n":0,"v":-0.119125},{"f":-1,"t":0,"y":0,"n":0,"v":-0.12998},{"f":-1,"t":0,"y":0,"n":0,"v":-0.125232},{"f":11,"t":1,"y":12,"n":25,"v":0},{"f":9,"t":3,"y":13,"n":18,"v":0},{"f":2,"t":3,"y":14,"n":15,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.07023},{"f":6,"t":3,"y":16,"n":17,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.082875},{"f":-1,"t":0,"y":0,"n":0,"v":0.110778},{"f":15,"t":2,"y":19,"n":22,"v":0},{"f":27,"t":1,"y":20,"n":21,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.107038},{"f":-1,"t":0,"y":0,"n":0,"v":0.013351},{"f":25,"t":1,"y":23,"n":24,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.030529},{"f":-1,"t":0,"y":0,"n":0,"v":0.115054},{"f":-1,"t":0,"y":0,"n":0,"v":-0.123642},{"f":11,"t":1,"y":27,"n":36,"v":0},{"f":6,"t":6,"y":28,"n":35,"v":0},{"f":14,"t":3,"y":29,"n":34,"v":0},{"f":18,"t":4,"y":30,"n":33,"v":0},{"f":9,"t":4,"y":31,"n":32,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.118541},{"f":-1,"t":0,"y":0,"n":0,"v":0.052778},{"f":-1,"t":0,"y":0,"n":0,"v":-0.023891},{"f":-1,"t":0,"y":0,"n":0,"v":-0.099574},{"f":-1,"t":0,"y":0,"n":0,"v":0.124699},{"f":-1,"t":0,"y":0,"n":0,"v":-0.110215}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.033674},{"f":-1,"t":0,"y":0,"n":0,"v":-0.126251},{"f":-1,"t":0,"y":0,"n":0,"v":0.121893},{"f":-1,"t":0,"y":0,"n":0,"v":-0.123543}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":2,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.026005},{"f":-1,"t":0,"y":0,"n":0,"v":-0.122481},{"f":-1,"t":0,"y":0,"n":0,"v":0.119334},{"f":-1,"t":0,"y":0,"n":0,"v":-0.120804}],[{"f":6,"t":3,"y":1,"n":12,"v":0},{"f":4,"t":1,"y":2,"n":11,"v":0},{"f":3,"t":1,"y":3,"n":10,"v":0},{"f":9,"t":1,"y":4,"n":9,"v":0},{"f":5,"t":1,"y":5,"n":8,"v":0},{"f":7,"t":1,"y":6,"n":7,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.066989},{"f":-1,"t":0,"y":0,"n":0,"v":-0.119292},{"f":-1,"t":0,"y":0,"n":0,"v":0.1009},{"f":-1,"t":0,"y":0,"n":0,"v":-0.117542},{"f":-1,"t":0,"y":0,"n":0,"v":0.112724},{"f":-1,"t":0,"y":0,"n":0,"v":-0.117537},{"f":11,"t":1,"y":13,"n":24,"v":0},{"f":6,"t":6,"y":14,"n":23,"v":0},{"f":5,"t":1,"y":15,"n":18,"v":0},{"f":3,"t":1,"y":16,"n":17,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.117232},{"f":-1,"t":0,"y":0,"n":0,"v":0.105563},{"f":14,"t":1,"y":19,"n":22,"v":0},{"f":8,"t":2,"y":20,"n":21,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.097505},{"f":-1,"t":0,"y":0,"n":0,"v":0.11452},{"f":-1,"t":0,"y":0,"n":0,"v":0.008878},{"f":-1,"t":0,"y":0,"n":0,"v":0.117101},{"f":-1,"t":0,"y":0,"n":0,"v":-0.115938}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":2,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.029886},{"f":-1,"t":0,"y":0,"n":0,"v":-0.116787},{"f":-1,"t":0,"y":0,"n":0,"v":0.11528},{"f":-1,"t":0,"y":0,"n":0,"v":-0.116945}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.021102},{"f":-1,"t":0,"y":0,"n":0,"v":0.113632},{"f":-1,"t":0,"y":0,"n":0,"v":-0.115084}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.023093},{"f":-1,"t":0,"y":0,"n":0,"v":-0.114995},{"f":-1,"t":0,"y":0,"n":0,"v":0.112018},{"f":-1,"t":0,"y":0,"n":0,"v":-0.113463}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.021435},{"f":-1,"t":0,"y":0,"n":0,"v":-0.112506},{"f":-1,"t":0,"y":0,"n":0,"v":0.110807},{"f":-1,"t":0,"y":0,"n":0,"v":-0.111999}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.021613},{"f":-1,"t":0,"y":0,"n":0,"v":-0.110627},{"f":-1,"t":0,"y":0,"n":0,"v":0.109574},{"f":-1,"t":0,"y":0,"n":0,"v":-0.110723}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.022198},{"f":-1,"t":0,"y":0,"n":0,"v":-0.108767},{"f":-1,"t":0,"y":0,"n":0,"v":0.108453},{"f":-1,"t":0,"y":0,"n":0,"v":-0.109588}],[{"f":1,"t":5,"y":1,"n":14,"v":0},{"f":10,"t":1,"y":2,"n":11,"v":0},{"f":4,"t":1,"y":3,"n":10,"v":0},{"f":3,"t":1,"y":4,"n":9,"v":0},{"f":1,"t":1,"y":5,"n":8,"v":0},{"f":7,"t":1,"y":6,"n":7,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.046418},{"f":-1,"t":0,"y":0,"n":0,"v":-0.107087},{"f":-1,"t":0,"y":0,"n":0,"v":-0.10833},{"f":-1,"t":0,"y":0,"n":0,"v":0.101423},{"f":-1,"t":0,"y":0,"n":0,"v":-0.108002},{"f":11,"t":1,"y":12,"n":13,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.086705},{"f":-1,"t":0,"y":0,"n":0,"v":-0.105347},{"f":1,"t":9,"y":15,"n":18,"v":0},{"f":5,"t":2,"y":16,"n":17,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.062312},{"f":-1,"t":0,"y":0,"n":0,"v":0.093939},{"f":18,"t":6,"y":19,"n":20,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.107482},{"f":1,"t":90,"y":21,"n":22,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.025131},{"f":-1,"t":0,"y":0,"n":0,"v":0.092991}],[{"f":1,"t":5,"y":1,"n":14,"v":0},{"f":4,"t":1,"y":2,"n":13,"v":0},{"f":5,"t":1,"y":3,"n":10,"v":0},{"f":3,"t":1,"y":4,"n":9,"v":0},{"f":1,"t":1,"y":5,"n":8,"v":0},{"f":7,"t":1,"y":6,"n":7,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.04076},{"f":-1,"t":0,"y":0,"n":0,"v":-0.105715},{"f":-1,"t":0,"y":0,"n":0,"v":-0.107229},{"f":-1,"t":0,"y":0,"n":0,"v":0.098057},{"f":11,"t":1,"y":11,"n":12,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.085153},{"f":-1,"t":0,"y":0,"n":0,"v":-0.101203},{"f":-1,"t":0,"y":0,"n":0,"v":-0.107192},{"f":6,"t":6,"y":15,"n":20,"v":0},{"f":5,"t":2,"y":16,"n":19,"v":0},{"f":3,"t":1,"y":17,"n":18,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.094571},{"f":-1,"t":0,"y":0,"n":0,"v":0.022625},{"f":-1,"t":0,"y":0,"n":0,"v":0.087403},{"f":-1,"t":0,"y":0,"n":0,"v":0.106893}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.027722},{"f":-1,"t":0,"y":0,"n":0,"v":-0.10433},{"f":-1,"t":0,"y":0,"n":0,"v":0.105961},{"f":-1,"t":0,"y":0,"n":0,"v":-0.107632}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.024552},{"f":-1,"t":0,"y":0,"n":0,"v":-0.102988},{"f":-1,"t":0,"y":0,"n":0,"v":0.105362},{"f":-1,"t":0,"y":0,"n":0,"v":-0.106852}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":2,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.018426},{"f":-1,"t":0,"y":0,"n":0,"v":-0.101567},{"f":-1,"t":0,"y":0,"n":0,"v":0.104741},{"f":-1,"t":0,"y":0,"n":0,"v":-0.106146}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.017335},{"f":-1,"t":0,"y":0,"n":0,"v":-0.100818},{"f":-1,"t":0,"y":0,"n":0,"v":0.104173},{"f":-1,"t":0,"y":0,"n":0,"v":-0.105523}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.018273},{"f":-1,"t":0,"y":0,"n":0,"v":-0.099411},{"f":-1,"t":0,"y":0,"n":0,"v":0.103791},{"f":-1,"t":0,"y":0,"n":0,"v":-0.10493}],[{"f":1,"t":5,"y":1,"n":14,"v":0},{"f":4,"t":1,"y":2,"n":13,"v":0},{"f":5,"t":1,"y":3,"n":10,"v":0},{"f":3,"t":1,"y":4,"n":9,"v":0},{"f":1,"t":1,"y":5,"n":8,"v":0},{"f":7,"t":1,"y":6,"n":7,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.032564},{"f":-1,"t":0,"y":0,"n":0,"v":-0.098053},{"f":-1,"t":0,"y":0,"n":0,"v":-0.106692},{"f":-1,"t":0,"y":0,"n":0,"v":0.091536},{"f":11,"t":1,"y":11,"n":12,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.077321},{"f":-1,"t":0,"y":0,"n":0,"v":-0.093958},{"f":-1,"t":0,"y":0,"n":0,"v":-0.103987},{"f":6,"t":6,"y":15,"n":20,"v":0},{"f":5,"t":2,"y":16,"n":17,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.047188},{"f":15,"t":3,"y":18,"n":19,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.084242},{"f":-1,"t":0,"y":0,"n":0,"v":0.044127},{"f":-1,"t":0,"y":0,"n":0,"v":0.103513}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.01902},{"f":-1,"t":0,"y":0,"n":0,"v":-0.097488},{"f":-1,"t":0,"y":0,"n":0,"v":0.102764},{"f":-1,"t":0,"y":0,"n":0,"v":-0.104236}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.014549},{"f":-1,"t":0,"y":0,"n":0,"v":-0.096213},{"f":-1,"t":0,"y":0,"n":0,"v":0.102349},{"f":-1,"t":0,"y":0,"n":0,"v":-0.103801}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.016568},{"f":-1,"t":0,"y":0,"n":0,"v":-0.095081},{"f":-1,"t":0,"y":0,"n":0,"v":0.101765},{"f":-1,"t":0,"y":0,"n":0,"v":-0.103392}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":6,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.013442},{"f":-1,"t":0,"y":0,"n":0,"v":-0.093937},{"f":-1,"t":0,"y":0,"n":0,"v":0.101595},{"f":-1,"t":0,"y":0,"n":0,"v":-0.102998}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.013649},{"f":-1,"t":0,"y":0,"n":0,"v":-0.092676},{"f":-1,"t":0,"y":0,"n":0,"v":0.101264},{"f":-1,"t":0,"y":0,"n":0,"v":-0.102679}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.007743},{"f":-1,"t":0,"y":0,"n":0,"v":-0.091593},{"f":-1,"t":0,"y":0,"n":0,"v":0.101211},{"f":-1,"t":0,"y":0,"n":0,"v":-0.10238}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":2,"t":3,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.011271},{"f":-1,"t":0,"y":0,"n":0,"v":-0.090304},{"f":-1,"t":0,"y":0,"n":0,"v":0.100909},{"f":-1,"t":0,"y":0,"n":0,"v":-0.102087}],[{"f":1,"t":5,"y":1,"n":14,"v":0},{"f":4,"t":1,"y":2,"n":13,"v":0},{"f":5,"t":1,"y":3,"n":10,"v":0},{"f":3,"t":1,"y":4,"n":9,"v":0},{"f":1,"t":1,"y":5,"n":8,"v":0},{"f":7,"t":1,"y":6,"n":7,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.017879},{"f":-1,"t":0,"y":0,"n":0,"v":-0.088443},{"f":-1,"t":0,"y":0,"n":0,"v":-0.109286},{"f":-1,"t":0,"y":0,"n":0,"v":0.082224},{"f":13,"t":1,"y":11,"n":12,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.068141},{"f":-1,"t":0,"y":0,"n":0,"v":-0.083152},{"f":-1,"t":0,"y":0,"n":0,"v":-0.102207},{"f":6,"t":6,"y":15,"n":18,"v":0},{"f":2,"t":9,"y":16,"n":17,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.071793},{"f":-1,"t":0,"y":0,"n":0,"v":-0.008453},{"f":-1,"t":0,"y":0,"n":0,"v":0.101179}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.006758},{"f":-1,"t":0,"y":0,"n":0,"v":-0.088443},{"f":-1,"t":0,"y":0,"n":0,"v":0.10003},{"f":-1,"t":0,"y":0,"n":0,"v":-0.101761}],[{"f":1,"t":5,"y":1,"n":14,"v":0},{"f":4,"t":1,"y":2,"n":13,"v":0},{"f":5,"t":1,"y":3,"n":10,"v":0},{"f":1,"t":1,"y":4,"n":7,"v":0},{"f":7,"t":1,"y":5,"n":6,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.008648},{"f":-1,"t":0,"y":0,"n":0,"v":-0.086637},{"f":3,"t":1,"y":8,"n":9,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.110782},{"f":-1,"t":0,"y":0,"n":0,"v":0.079698},{"f":19,"t":1,"y":11,"n":12,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.041031},{"f":-1,"t":0,"y":0,"n":0,"v":0.073627},{"f":-1,"t":0,"y":0,"n":0,"v":-0.101991},{"f":1,"t":9,"y":15,"n":18,"v":0},{"f":9,"t":3,"y":16,"n":17,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.068487},{"f":-1,"t":0,"y":0,"n":0,"v":0.009985},{"f":-1,"t":0,"y":0,"n":0,"v":0.099853}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":1,"t":1,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.009683},{"f":-1,"t":0,"y":0,"n":0,"v":-0.084594},{"f":-1,"t":0,"y":0,"n":0,"v":0.099913},{"f":-1,"t":0,"y":0,"n":0,"v":-0.101498}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":2,"t":3,"y":2,"n":5,"v":0},{"f":7,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.012524},{"f":-1,"t":0,"y":0,"n":0,"v":-0.084384},{"f":-1,"t":0,"y":0,"n":0,"v":0.099621},{"f":-1,"t":0,"y":0,"n":0,"v":-0.101266}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.008028},{"f":-1,"t":0,"y":0,"n":0,"v":0.099348},{"f":-1,"t":0,"y":0,"n":0,"v":-0.101081}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":1,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.00906},{"f":-1,"t":0,"y":0,"n":0,"v":0.099124},{"f":-1,"t":0,"y":0,"n":0,"v":-0.100882}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":2,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.009084},{"f":-1,"t":0,"y":0,"n":0,"v":0.098828},{"f":-1,"t":0,"y":0,"n":0,"v":-0.10069}],[{"f":1,"t":5,"y":1,"n":8,"v":0},{"f":4,"t":1,"y":2,"n":7,"v":0},{"f":5,"t":1,"y":3,"n":6,"v":0},{"f":12,"t":0.51,"y":4,"n":5,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.009047},{"f":-1,"t":0,"y":0,"n":0,"v":-0.072502},{"f":-1,"t":0,"y":0,"n":0,"v":0.042948},{"f":-1,"t":0,"y":0,"n":0,"v":-0.101101},{"f":6,"t":6,"y":9,"n":10,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.041537},{"f":-1,"t":0,"y":0,"n":0,"v":0.099723}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.004616},{"f":-1,"t":0,"y":0,"n":0,"v":0.098232},{"f":-1,"t":0,"y":0,"n":0,"v":-0.100488}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":1,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.006863},{"f":-1,"t":0,"y":0,"n":0,"v":0.097879},{"f":-1,"t":0,"y":0,"n":0,"v":-0.100322}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.006221},{"f":-1,"t":0,"y":0,"n":0,"v":0.097599},{"f":-1,"t":0,"y":0,"n":0,"v":-0.100143}],[{"f":6,"t":6,"y":1,"n":12,"v":0},{"f":4,"t":1,"y":2,"n":11,"v":0},{"f":10,"t":1,"y":3,"n":8,"v":0},{"f":6,"t":2,"y":4,"n":7,"v":0},{"f":1,"t":1,"y":5,"n":6,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.003931},{"f":-1,"t":0,"y":0,"n":0,"v":-0.02659},{"f":-1,"t":0,"y":0,"n":0,"v":-0.071719},{"f":8,"t":2,"y":9,"n":10,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.03437},{"f":-1,"t":0,"y":0,"n":0,"v":0.071294},{"f":-1,"t":0,"y":0,"n":0,"v":-0.10067},{"f":-1,"t":0,"y":0,"n":0,"v":0.099017}],[{"f":6,"t":6,"y":1,"n":12,"v":0},{"f":4,"t":1,"y":2,"n":11,"v":0},{"f":10,"t":1,"y":3,"n":8,"v":0},{"f":12,"t":0.51,"y":4,"n":7,"v":0},{"f":7,"t":1,"y":5,"n":6,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.005971},{"f":-1,"t":0,"y":0,"n":0,"v":-0.083438},{"f":-1,"t":0,"y":0,"n":0,"v":-0.0712},{"f":8,"t":2,"y":9,"n":10,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.033137},{"f":-1,"t":0,"y":0,"n":0,"v":0.067336},{"f":-1,"t":0,"y":0,"n":0,"v":-0.101266},{"f":-1,"t":0,"y":0,"n":0,"v":0.098815}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":1,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.004883},{"f":-1,"t":0,"y":0,"n":0,"v":0.096699},{"f":-1,"t":0,"y":0,"n":0,"v":-0.099913}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":2,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.011426},{"f":-1,"t":0,"y":0,"n":0,"v":0.097535},{"f":-1,"t":0,"y":0,"n":0,"v":-0.099743}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.008326},{"f":-1,"t":0,"y":0,"n":0,"v":0.095696},{"f":-1,"t":0,"y":0,"n":0,"v":-0.099579}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.005312},{"f":-1,"t":0,"y":0,"n":0,"v":0.09682},{"f":-1,"t":0,"y":0,"n":0,"v":-0.099396}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.000559},{"f":-1,"t":0,"y":0,"n":0,"v":0.094723},{"f":-1,"t":0,"y":0,"n":0,"v":-0.099219}],[{"f":1,"t":5,"y":1,"n":8,"v":0},{"f":4,"t":1,"y":2,"n":7,"v":0},{"f":10,"t":1,"y":3,"n":6,"v":0},{"f":9,"t":1,"y":4,"n":5,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.006157},{"f":-1,"t":0,"y":0,"n":0,"v":-0.050257},{"f":-1,"t":0,"y":0,"n":0,"v":0.033647},{"f":-1,"t":0,"y":0,"n":0,"v":-0.099836},{"f":-1,"t":0,"y":0,"n":0,"v":0.092092}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":1,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.003115},{"f":-1,"t":0,"y":0,"n":0,"v":0.09565},{"f":-1,"t":0,"y":0,"n":0,"v":-0.098995}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":2,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.004702},{"f":-1,"t":0,"y":0,"n":0,"v":0.0953},{"f":-1,"t":0,"y":0,"n":0,"v":-0.098789}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.003084},{"f":-1,"t":0,"y":0,"n":0,"v":0.092229},{"f":-1,"t":0,"y":0,"n":0,"v":-0.098583}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.001356},{"f":-1,"t":0,"y":0,"n":0,"v":0.091369},{"f":-1,"t":0,"y":0,"n":0,"v":-0.09835}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.000811},{"f":-1,"t":0,"y":0,"n":0,"v":0.093693},{"f":-1,"t":0,"y":0,"n":0,"v":-0.098111}],[{"f":6,"t":6,"y":1,"n":8,"v":0},{"f":7,"t":1,"y":2,"n":7,"v":0},{"f":9,"t":1,"y":3,"n":6,"v":0},{"f":10,"t":1,"y":4,"n":5,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.000275},{"f":-1,"t":0,"y":0,"n":0,"v":0.025253},{"f":-1,"t":0,"y":0,"n":0,"v":-0.048836},{"f":-1,"t":0,"y":0,"n":0,"v":-0.084614},{"f":-1,"t":0,"y":0,"n":0,"v":0.095222}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.004045},{"f":-1,"t":0,"y":0,"n":0,"v":0.092536},{"f":-1,"t":0,"y":0,"n":0,"v":-0.097846}],[{"f":0,"t":247,"y":1,"n":6,"v":0},{"f":3,"t":1,"y":2,"n":5,"v":0},{"f":5,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":5.8e-05},{"f":-1,"t":0,"y":0,"n":0,"v":0.083196},{"f":-1,"t":0,"y":0,"n":0,"v":0.09506},{"f":-1,"t":0,"y":0,"n":0,"v":-0.097564}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.000536},{"f":-1,"t":0,"y":0,"n":0,"v":0.091},{"f":-1,"t":0,"y":0,"n":0,"v":-0.097261}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":3,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.002555},{"f":-1,"t":0,"y":0,"n":0,"v":0.094122},{"f":-1,"t":0,"y":0,"n":0,"v":-0.096948}],[{"f":3,"t":1,"y":1,"n":6,"v":0},{"f":4,"t":1,"y":2,"n":5,"v":0},{"f":5,"t":1,"y":3,"n":4,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.00078},{"f":-1,"t":0,"y":0,"n":0,"v":0.038804},{"f":-1,"t":0,"y":0,"n":0,"v":-0.094261},{"f":-1,"t":0,"y":0,"n":0,"v":0.087784}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.0058},{"f":-1,"t":0,"y":0,"n":0,"v":0.088701},{"f":-1,"t":0,"y":0,"n":0,"v":-0.096599}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":3,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.001426},{"f":-1,"t":0,"y":0,"n":0,"v":0.092457},{"f":-1,"t":0,"y":0,"n":0,"v":-0.096193}],[{"f":3,"t":1,"y":1,"n":4,"v":0},{"f":5,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.003209},{"f":-1,"t":0,"y":0,"n":0,"v":0.031896},{"f":-1,"t":0,"y":0,"n":0,"v":0.085013}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":5,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.001556},{"f":-1,"t":0,"y":0,"n":0,"v":0.091202},{"f":-1,"t":0,"y":0,"n":0,"v":-0.095837}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":4,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.003601},{"f":-1,"t":0,"y":0,"n":0,"v":0.090464},{"f":-1,"t":0,"y":0,"n":0,"v":-0.095378}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.002674},{"f":-1,"t":0,"y":0,"n":0,"v":0.083822},{"f":-1,"t":0,"y":0,"n":0,"v":-0.094893}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":1,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.001674},{"f":-1,"t":0,"y":0,"n":0,"v":0.082211},{"f":-1,"t":0,"y":0,"n":0,"v":-0.094404}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":5,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.000834},{"f":-1,"t":0,"y":0,"n":0,"v":0.088259},{"f":-1,"t":0,"y":0,"n":0,"v":-0.093893}],[{"f":3,"t":1,"y":1,"n":4,"v":0},{"f":2,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.005269},{"f":-1,"t":0,"y":0,"n":0,"v":-0.036392},{"f":-1,"t":0,"y":0,"n":0,"v":0.080925}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":3,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.000685},{"f":-1,"t":0,"y":0,"n":0,"v":0.086733},{"f":-1,"t":0,"y":0,"n":0,"v":-0.093248}],[{"f":4,"t":1,"y":1,"n":4,"v":0},{"f":3,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.001125},{"f":-1,"t":0,"y":0,"n":0,"v":0.08522},{"f":-1,"t":0,"y":0,"n":0,"v":-0.099498}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":5,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.001384},{"f":-1,"t":0,"y":0,"n":0,"v":0.085424},{"f":-1,"t":0,"y":0,"n":0,"v":-0.092467}],[{"f":3,"t":1,"y":1,"n":2,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.003939},{"f":-1,"t":0,"y":0,"n":0,"v":0.076745}],[{"f":3,"t":1,"y":1,"n":2,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.00093},{"f":-1,"t":0,"y":0,"n":0,"v":0.074932}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":2,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.000708},{"f":-1,"t":0,"y":0,"n":0,"v":0.073391},{"f":-1,"t":0,"y":0,"n":0,"v":-0.091725}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.001064},{"f":-1,"t":0,"y":0,"n":0,"v":0.057369},{"f":-1,"t":0,"y":0,"n":0,"v":-0.09104}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.000437},{"f":-1,"t":0,"y":0,"n":0,"v":0.055602},{"f":-1,"t":0,"y":0,"n":0,"v":-0.090227}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.002596},{"f":-1,"t":0,"y":0,"n":0,"v":0.053598},{"f":-1,"t":0,"y":0,"n":0,"v":-0.089376}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.000134},{"f":-1,"t":0,"y":0,"n":0,"v":0.066822},{"f":-1,"t":0,"y":0,"n":0,"v":-0.088497}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":3,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.00145},{"f":-1,"t":0,"y":0,"n":0,"v":0.065197},{"f":-1,"t":0,"y":0,"n":0,"v":-0.087546}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":1,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.002205},{"f":-1,"t":0,"y":0,"n":0,"v":0.037426},{"f":-1,"t":0,"y":0,"n":0,"v":-0.086515}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":2,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.001147},{"f":-1,"t":0,"y":0,"n":0,"v":0.036726},{"f":-1,"t":0,"y":0,"n":0,"v":-0.08543}],[{"f":2,"t":3,"y":1,"n":2,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.000983},{"f":-1,"t":0,"y":0,"n":0,"v":0.040875}],[{"f":0,"t":247,"y":1,"n":2,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.001742},{"f":-1,"t":0,"y":0,"n":0,"v":-0.084391}],[{"f":0,"t":247,"y":1,"n":2,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.002619},{"f":-1,"t":0,"y":0,"n":0,"v":-0.083286}],[{"f":0,"t":247,"y":1,"n":4,"v":0},{"f":1,"t":1,"y":2,"n":3,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.00033},{"f":-1,"t":0,"y":0,"n":0,"v":0.032568},{"f":-1,"t":0,"y":0,"n":0,"v":-0.08209}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.003375}],[{"f":1,"t":1,"y":1,"n":2,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.001254},{"f":-1,"t":0,"y":0,"n":0,"v":0.043454}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.0005}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.001056}],[{"f":1,"t":1,"y":1,"n":2,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.003655},{"f":-1,"t":0,"y":0,"n":0,"v":0.026255}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.000518}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.001169}],[{"f":1,"t":1,"y":1,"n":2,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.004161},{"f":-1,"t":0,"y":0,"n":0,"v":0.008372}],[{"f":1,"t":1,"y":1,"n":2,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.00082},{"f":-1,"t":0,"y":0,"n":0,"v":0.023861}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.00109}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.001096}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.000711}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.000883}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.002565}],[{"f":1,"t":1,"y":1,"n":2,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.002175},{"f":-1,"t":0,"y":0,"n":0,"v":0.022166}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.000193}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.002949}],[{"f":1,"t":1,"y":1,"n":2,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.002481},{"f":-1,"t":0,"y":0,"n":0,"v":0.020291}],[{"f":-1,"t":0,"y":0,"n":0,"v":-9.6e-05}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.002123}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.000329}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.001707}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.000378}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.001883}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.001748}],[{"f":12,"t":0.17,"y":1,"n":2,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.001974},{"f":-1,"t":0,"y":0,"n":0,"v":-0.011073}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.003072}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.002935}],[{"f":1,"t":1,"y":1,"n":2,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":-0.00234},{"f":-1,"t":0,"y":0,"n":0,"v":0.018895}],[{"f":12,"t":0.25,"y":1,"n":2,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.002743},{"f":-1,"t":0,"y":0,"n":0,"v":-0.01085}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.002345}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.000839}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.003219}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.002346}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.001383}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.001845}],[{"f":1,"t":1,"y":1,"n":2,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.002236},{"f":-1,"t":0,"y":0,"n":0,"v":0.018535}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.004369}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.004391}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.000625}],[{"f":12,"t":0.07,"y":1,"n":2,"v":0},{"f":-1,"t":0,"y":0,"n":0,"v":0.001467},{"f":-1,"t":0,"y":0,"n":0,"v":-0.012558}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.001865}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.00056}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.00092}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.001952}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.002477}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.001695}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.001402}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.001508}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.001024}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.003954}],[{"f":-1,"t":0,"y":0,"n":0,"v":0.001829}],[{"f":-1,"t":0,"y":0,"n":0,"v":-0.000125}]]};
|
package/src/monitor/daemon.js
CHANGED
|
@@ -2,9 +2,10 @@ const { execFileSync } = require('child_process');
|
|
|
2
2
|
const fs = require('fs');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const os = require('os');
|
|
5
|
+
const v8 = require('v8');
|
|
5
6
|
const { isDockerAvailable, SANDBOX_CONCURRENCY_MAX } = require('../sandbox/index.js');
|
|
6
7
|
const { setVerboseMode, isSandboxEnabled, isCanaryEnabled, isLlmDetectiveEnabled, getLlmDetectiveMode, DOWNLOADS_CACHE_TTL } = require('./classify.js');
|
|
7
|
-
const { loadState, saveState, loadDailyStats, saveDailyStats, purgeTarballCache, getParisHour, atomicWriteFileSync, saveNpmSeq } = require('./state.js');
|
|
8
|
+
const { loadState, saveState, loadDailyStats, saveDailyStats, purgeTarballCache, getParisHour, atomicWriteFileSync, saveNpmSeq, ALERTS_FILE } = require('./state.js');
|
|
8
9
|
const { isTemporalEnabled, isTemporalAstEnabled, isTemporalPublishEnabled, isTemporalMaintainerEnabled } = require('./temporal.js');
|
|
9
10
|
const { pendingGrouped, flushScopeGroup, sendDailyReport, DAILY_REPORT_HOUR, alertedPackageRules } = require('./webhook.js');
|
|
10
11
|
const { poll } = require('./ingestion.js');
|
|
@@ -24,10 +25,15 @@ const MAX_QUEUE_PERSIST_SIZE = 200_000; // Don't persist if queue > 200K items (
|
|
|
24
25
|
const MAX_RESTORE_QUEUE_SIZE = 100_000; // Cap restored queue at 100K items
|
|
25
26
|
|
|
26
27
|
// ─── Memory pressure circuit breaker ───
|
|
27
|
-
// Graduated response based on V8 heap usage
|
|
28
|
-
// Threat model: when GC thrashing starts (>90% heap), throughput drops to 0
|
|
29
|
-
// the queue grows unbounded because ingestion continues. Without a circuit
|
|
30
|
-
// the only recovery is OOM kill or manual restart
|
|
28
|
+
// Graduated response based on V8 heap usage against heap_size_limit.
|
|
29
|
+
// Threat model: when GC thrashing starts (>90% heap limit), throughput drops to 0
|
|
30
|
+
// and the queue grows unbounded because ingestion continues. Without a circuit
|
|
31
|
+
// breaker, the only recovery is OOM kill or manual restart.
|
|
32
|
+
//
|
|
33
|
+
// Denominator: v8.getHeapStatistics().heap_size_limit (NOT process.memoryUsage().heapTotal).
|
|
34
|
+
// V8 dynamically adjusts heapTotal so heapUsed/heapTotal is structurally 70-85%
|
|
35
|
+
// even when actual usage is 0.1% of the --max-old-space-size limit. heap_size_limit
|
|
36
|
+
// reflects the actual V8 ceiling (~3264MB with --max-old-space-size=3072).
|
|
31
37
|
//
|
|
32
38
|
// Levels:
|
|
33
39
|
// NONE (<75%) — normal operation
|
|
@@ -286,10 +292,21 @@ const MAX_ALERTED_PACKAGES = 5_000;
|
|
|
286
292
|
* Compute current memory pressure level from V8 heap usage.
|
|
287
293
|
* Returns one of MEMORY_PRESSURE_LEVELS and updates the module-level _memoryPressureLevel.
|
|
288
294
|
* Cheap call (~0.1ms) — safe to run every 2s in the main loop.
|
|
295
|
+
*
|
|
296
|
+
* IMPORTANT: Uses v8.getHeapStatistics().heap_size_limit as the denominator,
|
|
297
|
+
* NOT process.memoryUsage().heapTotal. V8 adjusts heapTotal dynamically so
|
|
298
|
+
* heapUsed/heapTotal is structurally 70-85% even when actual usage is 0.1%
|
|
299
|
+
* of the --max-old-space-size limit. This caused the initial v2.10.88 circuit
|
|
300
|
+
* breaker to trigger at ELEVATED/HIGH permanently in normal operation.
|
|
301
|
+
*
|
|
302
|
+
* heap_size_limit reflects the actual V8 ceiling:
|
|
303
|
+
* - With --max-old-space-size=3072: ~3264MB (3072 + new space overhead)
|
|
304
|
+
* - Without the flag: ~4288MB (V8 default on 64-bit)
|
|
289
305
|
*/
|
|
290
306
|
function computeMemoryPressure() {
|
|
291
307
|
const mem = process.memoryUsage();
|
|
292
|
-
const
|
|
308
|
+
const heapLimit = v8.getHeapStatistics().heap_size_limit;
|
|
309
|
+
const ratio = heapLimit > 0 ? mem.heapUsed / heapLimit : 0;
|
|
293
310
|
|
|
294
311
|
if (ratio >= MEMORY_THRESHOLD_EMERGENCY) {
|
|
295
312
|
_memoryPressureLevel = MEMORY_PRESSURE_LEVELS.EMERGENCY;
|
|
@@ -476,6 +493,15 @@ async function startMonitor(options, stats, dailyAlerts, recentlyScanned, downlo
|
|
|
476
493
|
╚════════════════════════════════════════════╝
|
|
477
494
|
`);
|
|
478
495
|
|
|
496
|
+
// Note: alerts file migrated from .json to .jsonl in v2.10.89
|
|
497
|
+
const oldAlertsJson = ALERTS_FILE.replace('.jsonl', '.json');
|
|
498
|
+
if (fs.existsSync(oldAlertsJson)) {
|
|
499
|
+
try {
|
|
500
|
+
const sizeMB = (fs.statSync(oldAlertsJson).size / 1024 / 1024).toFixed(0);
|
|
501
|
+
console.log(`[MONITOR] Legacy ${path.basename(oldAlertsJson)} found (${sizeMB}MB). Safe to archive — alerts now use JSONL.`);
|
|
502
|
+
} catch {}
|
|
503
|
+
}
|
|
504
|
+
|
|
479
505
|
// Check sandbox availability
|
|
480
506
|
if (isSandboxEnabled()) {
|
|
481
507
|
sandboxAvailableRef.value = isDockerAvailable();
|
|
@@ -547,6 +573,8 @@ async function startMonitor(options, stats, dailyAlerts, recentlyScanned, downlo
|
|
|
547
573
|
console.log('[MONITOR] npm changes stream enabled (replicate.npmjs.com) with RSS fallback');
|
|
548
574
|
console.log(`[MONITOR] Scan concurrency: adaptive ${BASE_CONCURRENCY}→${getTargetConcurrency()} (base MUADDIB_SCAN_CONCURRENCY=${BASE_CONCURRENCY}, max MUADDIB_MAX_CONCURRENCY)`);
|
|
549
575
|
console.log(`[MONITOR] Sandbox concurrency: ${SANDBOX_CONCURRENCY_MAX} (MUADDIB_SANDBOX_CONCURRENCY to override)`);
|
|
576
|
+
const heapLimitMB = (v8.getHeapStatistics().heap_size_limit / 1024 / 1024).toFixed(0);
|
|
577
|
+
console.log(`[MONITOR] Memory circuit breaker: heap limit ${heapLimitMB}MB, thresholds HIGH=${(MEMORY_THRESHOLD_HIGH * 100).toFixed(0)}% CRITICAL=${(MEMORY_THRESHOLD_CRITICAL * 100).toFixed(0)}% EMERGENCY=${(MEMORY_THRESHOLD_EMERGENCY * 100).toFixed(0)}%, GC=${typeof global.gc === 'function' ? 'available' : 'unavailable (start with --expose-gc)'}`);
|
|
550
578
|
console.log(`[MONITOR] Polling every ${POLL_INTERVAL / 1000}s (decoupled from processing). Ctrl+C to stop.\n`);
|
|
551
579
|
|
|
552
580
|
let running = true;
|
|
@@ -711,10 +739,11 @@ async function startMonitor(options, stats, dailyAlerts, recentlyScanned, downlo
|
|
|
711
739
|
|
|
712
740
|
if (Date.now() - lastMemoryLogTime >= memLogInterval) {
|
|
713
741
|
const heapUsedMB = (currentMem.heapUsed / 1024 / 1024).toFixed(0);
|
|
714
|
-
const
|
|
742
|
+
const heapLimitMB = (v8.getHeapStatistics().heap_size_limit / 1024 / 1024).toFixed(0);
|
|
715
743
|
const rssMB = (currentMem.rss / 1024 / 1024).toFixed(0);
|
|
744
|
+
const pctUsed = (heapRatio * 100).toFixed(0);
|
|
716
745
|
const levelName = Object.keys(MEMORY_PRESSURE_LEVELS).find(k => MEMORY_PRESSURE_LEVELS[k] === pressureLevel) || 'UNKNOWN';
|
|
717
|
-
console.log(`[MONITOR] MEMORY: heap=${heapUsedMB}MB/${
|
|
746
|
+
console.log(`[MONITOR] MEMORY: heap=${heapUsedMB}MB/${heapLimitMB}MB (${pctUsed}%), rss=${rssMB}MB, queue=${scanQueue.length}, dedup=${recentlyScanned.size}, downloads=${downloadsCache.size}, alerts=${alertedPackageRules.size}, pressure=${levelName}`);
|
|
718
747
|
|
|
719
748
|
// Graduated response at HIGH+
|
|
720
749
|
if (pressureLevel >= MEMORY_PRESSURE_LEVELS.HIGH) {
|
package/src/monitor/state.js
CHANGED
|
@@ -10,13 +10,17 @@ const { sanitizePackageName } = require('../shared/download.js');
|
|
|
10
10
|
// --- File path constants ---
|
|
11
11
|
|
|
12
12
|
const STATE_FILE = path.join(__dirname, '..', '..', 'data', 'monitor-state.json');
|
|
13
|
-
const ALERTS_FILE = path.join(__dirname, '..', '..', 'data', 'monitor-alerts.
|
|
13
|
+
const ALERTS_FILE = path.join(__dirname, '..', '..', 'data', 'monitor-alerts.jsonl');
|
|
14
14
|
const DETECTIONS_FILE = path.join(__dirname, '..', '..', 'data', 'detections.json');
|
|
15
15
|
const SCAN_STATS_FILE = path.join(__dirname, '..', '..', 'data', 'scan-stats.json');
|
|
16
16
|
const LAST_DAILY_REPORT_FILE = path.join(__dirname, '..', '..', 'data', 'last-daily-report.json');
|
|
17
17
|
const DAILY_STATS_FILE = path.join(__dirname, '..', '..', 'data', 'daily-stats.json');
|
|
18
18
|
const TEMPORAL_DETECTIONS_FILE = path.join(__dirname, '..', '..', 'data', 'temporal-detections.json');
|
|
19
19
|
|
|
20
|
+
// --- Alerts/detections persistence limits ---
|
|
21
|
+
const ALERTS_MAX_SIZE = 100 * 1024 * 1024; // 100MB rotation threshold (matches ml-training.jsonl)
|
|
22
|
+
const MAX_DETECTIONS = 10_000; // Cap detections array — oldest entries discarded
|
|
23
|
+
|
|
20
24
|
// Local log persistence directories (parallel to Discord webhooks for offline analysis)
|
|
21
25
|
// Primary: logs/ relative to project root. Fallback: /tmp/ if primary is read-only (EROFS/EACCES).
|
|
22
26
|
const PRIMARY_DAILY_REPORTS_DIR = path.join(__dirname, '..', '..', 'logs', 'daily-reports');
|
|
@@ -436,6 +440,27 @@ function purgeTarballCache() {
|
|
|
436
440
|
|
|
437
441
|
// --- Temporal detections ---
|
|
438
442
|
|
|
443
|
+
/**
|
|
444
|
+
* Trim temporal findings to essential fields only.
|
|
445
|
+
* Production findings arrive as { type, data: { suspicious, message, score, findings: [...], ... } }
|
|
446
|
+
* with the data object containing full AST diffs, metadata snapshots, etc (~80KB each).
|
|
447
|
+
* This retains only type, severity, suspicious, message, and score for persistence.
|
|
448
|
+
*/
|
|
449
|
+
function trimTemporalFindings(findings) {
|
|
450
|
+
return findings.map(f => {
|
|
451
|
+
const trimmed = { type: f.type };
|
|
452
|
+
if (f.severity) trimmed.severity = f.severity;
|
|
453
|
+
if (f.message) trimmed.message = f.message;
|
|
454
|
+
if (f.data) {
|
|
455
|
+
if (f.data.suspicious !== undefined) trimmed.suspicious = f.data.suspicious;
|
|
456
|
+
if (f.data.message) trimmed.message = trimmed.message || f.data.message;
|
|
457
|
+
if (f.data.score !== undefined) trimmed.score = f.data.score;
|
|
458
|
+
if (f.data.severity) trimmed.severity = trimmed.severity || f.data.severity;
|
|
459
|
+
}
|
|
460
|
+
return trimmed;
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
439
464
|
/**
|
|
440
465
|
* Append a temporal detection to the temporal detections file.
|
|
441
466
|
* @param {string} name - Package name
|
|
@@ -452,7 +477,7 @@ function appendTemporalDetection(name, version, findings) {
|
|
|
452
477
|
detections.push({
|
|
453
478
|
name,
|
|
454
479
|
version,
|
|
455
|
-
findings,
|
|
480
|
+
findings: trimTemporalFindings(findings),
|
|
456
481
|
timestamp: new Date().toISOString()
|
|
457
482
|
});
|
|
458
483
|
// Keep last 1000 entries
|
|
@@ -520,7 +545,21 @@ function saveState(state, stats) {
|
|
|
520
545
|
}
|
|
521
546
|
}
|
|
522
547
|
|
|
523
|
-
// --- Alerts persistence ---
|
|
548
|
+
// --- Alerts persistence (JSONL append-only) ---
|
|
549
|
+
|
|
550
|
+
function maybeRotateAlerts() {
|
|
551
|
+
try {
|
|
552
|
+
if (!fs.existsSync(ALERTS_FILE)) return;
|
|
553
|
+
const stat = fs.statSync(ALERTS_FILE);
|
|
554
|
+
if (stat.size < ALERTS_MAX_SIZE) return;
|
|
555
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
556
|
+
const rotatedName = ALERTS_FILE.replace('.jsonl', `-${timestamp}.jsonl`);
|
|
557
|
+
fs.renameSync(ALERTS_FILE, rotatedName);
|
|
558
|
+
console.log(`[MONITOR] Rotated alerts -> ${path.basename(rotatedName)} (${(stat.size / 1024 / 1024).toFixed(1)}MB)`);
|
|
559
|
+
} catch (err) {
|
|
560
|
+
console.error(`[MONITOR] Alerts rotation failed: ${err.message}`);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
524
563
|
|
|
525
564
|
function appendAlert(alert) {
|
|
526
565
|
try {
|
|
@@ -528,13 +567,14 @@ function appendAlert(alert) {
|
|
|
528
567
|
if (!fs.existsSync(dir)) {
|
|
529
568
|
fs.mkdirSync(dir, { recursive: true });
|
|
530
569
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
} catch {}
|
|
535
|
-
alerts.push(alert);
|
|
536
|
-
atomicWriteFileSync(ALERTS_FILE, JSON.stringify(alerts, null, 2));
|
|
570
|
+
maybeRotateAlerts();
|
|
571
|
+
const line = JSON.stringify(alert) + '\n';
|
|
572
|
+
fs.appendFileSync(ALERTS_FILE, line, 'utf8');
|
|
537
573
|
} catch (err) {
|
|
574
|
+
if (err.code === 'EROFS' || err.code === 'EACCES' || err.code === 'EPERM') {
|
|
575
|
+
console.warn(`[MONITOR] Permission denied writing alerts: ${err.code}`);
|
|
576
|
+
return;
|
|
577
|
+
}
|
|
538
578
|
console.error(`[MONITOR] Failed to save alert: ${err.message}`);
|
|
539
579
|
}
|
|
540
580
|
}
|
|
@@ -573,6 +613,10 @@ function appendDetection(name, version, ecosystem, findings, severity) {
|
|
|
573
613
|
advisory_at: null,
|
|
574
614
|
lead_time_hours: null
|
|
575
615
|
});
|
|
616
|
+
// Cap at MAX_DETECTIONS — discard oldest entries
|
|
617
|
+
if (data.detections.length > MAX_DETECTIONS) {
|
|
618
|
+
data.detections = data.detections.slice(-MAX_DETECTIONS);
|
|
619
|
+
}
|
|
576
620
|
atomicWriteFileSync(DETECTIONS_FILE, JSON.stringify(data, null, 2));
|
|
577
621
|
} catch (err) {
|
|
578
622
|
console.error(`[MONITOR] Failed to save detection: ${err.message}`);
|
|
@@ -846,6 +890,8 @@ module.exports = {
|
|
|
846
890
|
TARBALL_CACHE_HIGH_RISK_RETENTION_DAYS,
|
|
847
891
|
TARBALL_CACHE_MAX_SIZE_BYTES,
|
|
848
892
|
DAILY_STATS_PERSIST_INTERVAL,
|
|
893
|
+
ALERTS_MAX_SIZE,
|
|
894
|
+
MAX_DETECTIONS,
|
|
849
895
|
|
|
850
896
|
// Mutable state getters/setters
|
|
851
897
|
getScanMemoryCache,
|
|
@@ -844,6 +844,43 @@ const PLAYBOOKS = {
|
|
|
844
844
|
trusted_new_dependency:
|
|
845
845
|
'HAUTE: Package populaire (TRUSTED) a ajoute une nouvelle dependance connue. ' +
|
|
846
846
|
'Verifier le changelog et la legitimite de l\'ajout. Pas de blocage immediat mais surveillance renforcee.',
|
|
847
|
+
|
|
848
|
+
// v2.10.89: Security review findings (apr-2026)
|
|
849
|
+
curl_env_exfil:
|
|
850
|
+
'CRITIQUE: curl/wget exfiltre les variables d\'environnement ou des donnees systeme via lifecycle script. ' +
|
|
851
|
+
'Machine potentiellement compromise si deja installe. Rotation immediate de TOUS les secrets ' +
|
|
852
|
+
'(npm tokens, AWS keys, GitHub tokens, etc.). Verifier les connexions sortantes recentes.',
|
|
853
|
+
|
|
854
|
+
function_constructor_require:
|
|
855
|
+
'CRITIQUE: new Function.constructor("require", code) — execution de code dynamique avec acces au require reel. ' +
|
|
856
|
+
'Le code telecharge probablement un payload depuis un C2 (jsonkeeper.com, npoint.io, etc.) et l\'execute. ' +
|
|
857
|
+
'Isoler la machine. Supprimer le package. Regenerer TOUS les secrets.',
|
|
858
|
+
|
|
859
|
+
process_variable_shadow:
|
|
860
|
+
'ELEVE: Le global process est shadow par une variable locale pour cacher des URLs C2 dans process.env. ' +
|
|
861
|
+
'Technique d\'evasion de la campagne "Robert King" (npoint.io/jsonkeeper.com). ' +
|
|
862
|
+
'Verifier les URLs dans les valeurs de la fausse variable process. Supprimer le package.',
|
|
863
|
+
|
|
864
|
+
newsletter_auto_follow:
|
|
865
|
+
'ELEVE: Fork Baileys malveillant qui force l\'abonnement a des channels WhatsApp via la session authentifiee. ' +
|
|
866
|
+
'Les JIDs des channels sont hardcodes ou telecharges depuis un C2 (cdn.malvintech.sbs). ' +
|
|
867
|
+
'Desinstaller immediatement. Verifier les channels WhatsApp auxquels la session est abonnee. ' +
|
|
868
|
+
'Revoquer la session WhatsApp Web si compromise.',
|
|
869
|
+
|
|
870
|
+
version_99_preinstall:
|
|
871
|
+
'ELEVE: Version >= 99.x.x avec hook lifecycle — indicateur de dependency confusion. ' +
|
|
872
|
+
'La version elevee force npm a resoudre vers le package public au lieu du package interne prive. ' +
|
|
873
|
+
'NE PAS installer. Configurer un registry scope pour les packages internes. ' +
|
|
874
|
+
'Signaler le package sur npm comme dependency confusion.',
|
|
875
|
+
|
|
876
|
+
lifecycle_newsletter_hijack:
|
|
877
|
+
'CRITIQUE: Lifecycle hook + newsletter auto-follow WhatsApp — channel hijack a l\'installation. ' +
|
|
878
|
+
'Desinstaller immediatement. Revoquer la session WhatsApp Web. ' +
|
|
879
|
+
'Verifier les channels auxquels la session est abonnee.',
|
|
880
|
+
|
|
881
|
+
lifecycle_env_exfil:
|
|
882
|
+
'CRITIQUE: Lifecycle hook + exfiltration env via curl/wget a l\'installation. ' +
|
|
883
|
+
'Machine compromise si deja installe. Rotation immediate de TOUS les secrets.',
|
|
847
884
|
};
|
|
848
885
|
|
|
849
886
|
function getPlaybook(threatType) {
|
package/src/rules/index.js
CHANGED
|
@@ -2218,6 +2218,64 @@ const RULES = {
|
|
|
2218
2218
|
],
|
|
2219
2219
|
mitre: 'T1195.002'
|
|
2220
2220
|
},
|
|
2221
|
+
// v2.10.89: Security review findings (apr-2026) — 5 new rules from 14K tarball review
|
|
2222
|
+
curl_env_exfil: {
|
|
2223
|
+
id: 'MUADDIB-PKG-018',
|
|
2224
|
+
name: 'Curl/Wget Environment Exfiltration',
|
|
2225
|
+
severity: 'CRITICAL',
|
|
2226
|
+
confidence: 'high',
|
|
2227
|
+
description: 'curl/wget combine avec base64 ou env dans un lifecycle script — exfiltration de credentials a l\'installation. Pattern: curl -d $(env|base64) URL dans preinstall/postinstall.',
|
|
2228
|
+
references: [
|
|
2229
|
+
'https://attack.mitre.org/techniques/T1041/',
|
|
2230
|
+
'https://blog.phylum.io/npm-dependency-confusion-attacks'
|
|
2231
|
+
],
|
|
2232
|
+
mitre: 'T1041'
|
|
2233
|
+
},
|
|
2234
|
+
function_constructor_require: {
|
|
2235
|
+
id: 'MUADDIB-AST-086',
|
|
2236
|
+
name: 'Function Constructor Require Evasion',
|
|
2237
|
+
severity: 'CRITICAL',
|
|
2238
|
+
confidence: 'high',
|
|
2239
|
+
description: 'new Function.constructor("require", code) — execution de code dynamique via le constructeur Function avec acces au require reel. Technique d\'evasion: contourne la detection de eval/require en passant par le prototype de Function.',
|
|
2240
|
+
references: [
|
|
2241
|
+
'https://attack.mitre.org/techniques/T1059/007/'
|
|
2242
|
+
],
|
|
2243
|
+
mitre: 'T1059.007'
|
|
2244
|
+
},
|
|
2245
|
+
process_variable_shadow: {
|
|
2246
|
+
id: 'MUADDIB-AST-087',
|
|
2247
|
+
name: 'Process Variable Shadowing',
|
|
2248
|
+
severity: 'HIGH',
|
|
2249
|
+
confidence: 'high',
|
|
2250
|
+
description: 'Le global process est shadow par une variable locale (const process = {...}). Technique d\'evasion: cache les URLs C2 dans un faux process.env pour contourner la detection de domaines suspects. Campagne "Robert King" (npoint.io/jsonkeeper.com).',
|
|
2251
|
+
references: [
|
|
2252
|
+
'https://attack.mitre.org/techniques/T1036/'
|
|
2253
|
+
],
|
|
2254
|
+
mitre: 'T1036'
|
|
2255
|
+
},
|
|
2256
|
+
newsletter_auto_follow: {
|
|
2257
|
+
id: 'MUADDIB-AST-088',
|
|
2258
|
+
name: 'Baileys Newsletter Auto-Follow Hijack',
|
|
2259
|
+
severity: 'HIGH',
|
|
2260
|
+
confidence: 'high',
|
|
2261
|
+
description: 'Pattern de detournement WhatsApp Baileys: newsletter + FOLLOW/QueryIds ou AUTO_FOLLOW_CHANNELS dans le meme fichier. Force l\'abonnement a des channels WhatsApp via la session authentifiee de la victime sans consentement.',
|
|
2262
|
+
references: [
|
|
2263
|
+
'https://attack.mitre.org/techniques/T1496/'
|
|
2264
|
+
],
|
|
2265
|
+
mitre: 'T1496'
|
|
2266
|
+
},
|
|
2267
|
+
version_99_preinstall: {
|
|
2268
|
+
id: 'MUADDIB-PKG-019',
|
|
2269
|
+
name: 'Dependency Confusion Version Indicator',
|
|
2270
|
+
severity: 'HIGH',
|
|
2271
|
+
confidence: 'high',
|
|
2272
|
+
description: 'Version >= 99.x.x avec hook lifecycle (preinstall/postinstall). Indicateur fort de dependency confusion: la version elevee force la resolution npm vers le package public malveillant au lieu du package interne prive.',
|
|
2273
|
+
references: [
|
|
2274
|
+
'https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610',
|
|
2275
|
+
'https://attack.mitre.org/techniques/T1195.002/'
|
|
2276
|
+
],
|
|
2277
|
+
mitre: 'T1195.002'
|
|
2278
|
+
},
|
|
2221
2279
|
// Trusted dependency diff detections (monitor-only)
|
|
2222
2280
|
trusted_new_unknown_dependency: {
|
|
2223
2281
|
id: 'MUADDIB-TRUSTED-001',
|
|
@@ -158,7 +158,16 @@ const SUSPICIOUS_DOMAINS_HIGH = [
|
|
|
158
158
|
'scan.aquasecurtiy.org', // Trivy exfil C2 (typosquat of aquasecurity)
|
|
159
159
|
'api.telegram.org', // Telegram bot exfiltration (crypto typosquats)
|
|
160
160
|
'checkmarx.zone', // Checkmarx/LiteLLM exfil C2
|
|
161
|
-
'45.148.10.212', '83.142.209.11'
|
|
161
|
+
'45.148.10.212', '83.142.209.11', // TeamPCP C2 IPs
|
|
162
|
+
// Security review apr-2026 findings
|
|
163
|
+
'cdn.malvintech.sbs', // Baileys V3 C2 (newsletter JID dynamic loading)
|
|
164
|
+
'173.211.46.220', // RCE trojan campaign (react-emits, buffer-util-extend)
|
|
165
|
+
'144.31.107.231', // Strapi/Guardarian targeted attack C2
|
|
166
|
+
'cchubber-telemetry.asmirkhan087.workers.dev', // Claude Code credential stealer
|
|
167
|
+
'minhdong.site', // Facebook credential proxy (fca-mmtat)
|
|
168
|
+
'ltidi.storage.googleapis.com', // KuCoin dependency confusion payload
|
|
169
|
+
'jsonkeeper.com', // Robert King campaign C2 dead drop
|
|
170
|
+
'npoint.io' // Robert King campaign C2 dead drop
|
|
162
171
|
];
|
|
163
172
|
|
|
164
173
|
// Suspicious tunnel/proxy domains (MEDIUM severity)
|
|
@@ -23,6 +23,27 @@ function handleNewExpression(node, ctx) {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
// v2.10.89: new Function.constructor("require", code) — RCE evasion
|
|
27
|
+
// Catches: chai-as-inserted (score 10→CRITICAL), cookie-parseflow (score 10→CRITICAL)
|
|
28
|
+
// Pattern: new Function.constructor("require", s); handler(require) — passes real require to dynamic code
|
|
29
|
+
if (node.callee.type === 'MemberExpression' &&
|
|
30
|
+
node.callee.property?.type === 'Identifier' && node.callee.property.name === 'constructor' &&
|
|
31
|
+
node.callee.object?.type === 'Identifier' && node.callee.object.name === 'Function' &&
|
|
32
|
+
node.arguments.length >= 1) {
|
|
33
|
+
const hasRequireArg = node.arguments.some(arg =>
|
|
34
|
+
arg.type === 'Literal' && typeof arg.value === 'string' && arg.value === 'require'
|
|
35
|
+
);
|
|
36
|
+
if (hasRequireArg) {
|
|
37
|
+
ctx.hasDynamicExec = true;
|
|
38
|
+
ctx.threats.push({
|
|
39
|
+
type: 'function_constructor_require',
|
|
40
|
+
severity: 'CRITICAL',
|
|
41
|
+
message: 'new Function.constructor("require", ...) — dynamic code execution bypassing require() detection. Attacker passes real require to execute arbitrary code.',
|
|
42
|
+
file: ctx.relFile
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
26
47
|
// Batch 1: new vm.Script(code) — dynamic code compilation via vm module
|
|
27
48
|
if (node.callee.type === 'MemberExpression' &&
|
|
28
49
|
node.callee.property?.type === 'Identifier' && node.callee.property.name === 'Script' &&
|
|
@@ -488,6 +488,28 @@ function handlePostWalk(ctx) {
|
|
|
488
488
|
});
|
|
489
489
|
}
|
|
490
490
|
}
|
|
491
|
+
|
|
492
|
+
// v2.10.89: Baileys newsletter auto-follow hijack
|
|
493
|
+
// Catches: Baileys V4 (budetzz score 35→HIGH, archeron-dev score 35→HIGH), all Baileys variants
|
|
494
|
+
// Pattern: newsletter + (FOLLOW|QueryIds|AUTO_FOLLOW_CHANNELS) in same file
|
|
495
|
+
// Uses source code regex since these are string patterns, not AST node types
|
|
496
|
+
const src = ctx._sourceCode || '';
|
|
497
|
+
if (/\bnewsletter\b/i.test(src) &&
|
|
498
|
+
(/\bFOLLOW\b/.test(src) || /\bQueryIds\b/.test(src) || /\bAUTO_FOLLOW_CHANNELS\b/.test(src))) {
|
|
499
|
+
// Only fire if the file also has WhatsApp/Baileys context to avoid FP on email newsletter code
|
|
500
|
+
const hasBaileysContext = /\b(baileys|whatsapp|WAProto|WA\.|SignalProtocol|libsignal|newsletterWMex)\b/i.test(src);
|
|
501
|
+
if (hasBaileysContext) {
|
|
502
|
+
const hasDelay = ctx.hasTimerDelayedPayload || /\bsetTimeout\b/.test(src) || /\bsetInterval\b/.test(src);
|
|
503
|
+
ctx.threats.push({
|
|
504
|
+
type: 'newsletter_auto_follow',
|
|
505
|
+
severity: hasDelay ? 'CRITICAL' : 'HIGH',
|
|
506
|
+
message: hasDelay
|
|
507
|
+
? 'Baileys newsletter auto-follow with timer delay — forces WhatsApp channel subscription via authenticated session. Timer evasion pattern.'
|
|
508
|
+
: 'Baileys newsletter auto-follow pattern — forces WhatsApp channel subscription via authenticated session without user consent.',
|
|
509
|
+
file: ctx.relFile
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
}
|
|
491
513
|
}
|
|
492
514
|
|
|
493
515
|
|
|
@@ -78,6 +78,18 @@ function handleVariableDeclarator(node, ctx) {
|
|
|
78
78
|
ctx.varSource.set(node.id.name, source);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
// v2.10.89: Detect process variable shadowing — evasion technique
|
|
82
|
+
// Catches: Robert King campaign (process shadow to hide C2 URLs in fake process.env)
|
|
83
|
+
// Pattern: const process = { env: { DEV_API_KEY: "https://evil.com/..." } }
|
|
84
|
+
if (node.id.name === 'process' && node.init?.type === 'ObjectExpression') {
|
|
85
|
+
ctx.threats.push({
|
|
86
|
+
type: 'process_variable_shadow',
|
|
87
|
+
severity: 'HIGH',
|
|
88
|
+
message: 'Global "process" shadowed with local ObjectExpression — evasion technique to hide C2 URLs in fake process.env.',
|
|
89
|
+
file: ctx.relFile
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
81
93
|
// Track dynamic require vars + module aliases
|
|
82
94
|
if (node.init?.type === 'CallExpression') {
|
|
83
95
|
const initCallName = getCallName(node.init);
|
package/src/scanner/package.js
CHANGED
|
@@ -116,6 +116,22 @@ async function scanPackageJson(targetPath) {
|
|
|
116
116
|
});
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
// v2.10.89: curl/wget + env/base64 exfiltration in lifecycle scripts
|
|
120
|
+
// Catches: apache-arrow-14 (score 9→CRITICAL), @signals-notebook (score 9→CRITICAL)
|
|
121
|
+
// Pattern: curl -d $(env|base64) URL, curl -X POST URL?env=$(env|base64 -w0)
|
|
122
|
+
if (['preinstall', 'install', 'postinstall'].includes(scriptName) &&
|
|
123
|
+
/\b(curl|wget)\b/.test(scriptContent) &&
|
|
124
|
+
(/\$\(.*\b(env|id|whoami|uname|hostname)\b/.test(scriptContent) ||
|
|
125
|
+
(/\bbase64\b/.test(scriptContent) && !/\|\s*(sh|bash)\b/.test(scriptContent)))) {
|
|
126
|
+
// Exclude curl|sh which is already caught by lifecycle_shell_pipe
|
|
127
|
+
threats.push({
|
|
128
|
+
type: 'curl_env_exfil',
|
|
129
|
+
severity: 'CRITICAL',
|
|
130
|
+
message: `Critical: "${scriptName}" uses curl/wget with env/base64 exfiltration — credential theft via lifecycle script.`,
|
|
131
|
+
file: 'package.json'
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
119
135
|
// Detect Bun runtime evasion in lifecycle scripts (Shai-Hulud 2.0)
|
|
120
136
|
if (/\bbun\s+(run|exec|install|x)\b/.test(scriptContent) || /\bbunx\s+/.test(scriptContent)) {
|
|
121
137
|
threats.push({
|
|
@@ -147,6 +163,22 @@ async function scanPackageJson(targetPath) {
|
|
|
147
163
|
}
|
|
148
164
|
}
|
|
149
165
|
|
|
166
|
+
// v2.10.89: Dependency confusion indicator — version >= 99 with install hooks
|
|
167
|
+
// Catches: @corpweb-ui/wmkt-library, @toprank/partner, @adac-fahrzeugplattform/ui
|
|
168
|
+
const versionStr = pkg.version || '';
|
|
169
|
+
const majorVersion = parseInt(versionStr.split('.')[0], 10);
|
|
170
|
+
if (majorVersion >= 99) {
|
|
171
|
+
const hasInstallHook = ['preinstall', 'install', 'postinstall'].some(s => scripts[s]);
|
|
172
|
+
if (hasInstallHook) {
|
|
173
|
+
threats.push({
|
|
174
|
+
type: 'version_99_preinstall',
|
|
175
|
+
severity: 'HIGH',
|
|
176
|
+
message: `Version ${versionStr} (major >= 99) with lifecycle hook — dependency confusion attack pattern.`,
|
|
177
|
+
file: 'package.json'
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
150
182
|
// Check non-lifecycle scripts (test, start, etc.) for network exfil commands
|
|
151
183
|
const NETWORK_SCRIPT_PATTERN = /\bcurl\b|\bwget\b|\bnc\s+-|\bncat\b|\bpowershell\b|\bnslookup\b/;
|
|
152
184
|
for (const [scriptName, scriptContent] of Object.entries(scripts)) {
|
package/src/scoring.js
CHANGED
|
@@ -115,7 +115,10 @@ const PACKAGE_LEVEL_TYPES = new Set([
|
|
|
115
115
|
// Blue Team v8: package-level boost signals
|
|
116
116
|
'isolated_suspicious_file', 'deep_suspicious_file',
|
|
117
117
|
// Blue Team v8b: phantom lifecycle scripts
|
|
118
|
-
'lifecycle_missing_script'
|
|
118
|
+
'lifecycle_missing_script',
|
|
119
|
+
// v2.10.89: Security review compounds
|
|
120
|
+
'lifecycle_newsletter_hijack', 'lifecycle_env_exfil',
|
|
121
|
+
'curl_env_exfil', 'version_99_preinstall'
|
|
119
122
|
]);
|
|
120
123
|
|
|
121
124
|
/**
|
|
@@ -248,6 +251,8 @@ const DIST_EXEMPT_TYPES = new Set([
|
|
|
248
251
|
// Kept in REACHABILITY_EXEMPT_TYPES (lifecycle invocation is valid).
|
|
249
252
|
'node_modules_write', // writeFile to node_modules/ (worm propagation)
|
|
250
253
|
'npm_publish_worm', // exec("npm publish") (worm propagation)
|
|
254
|
+
'curl_env_exfil', // curl/wget env exfil in lifecycle (always malicious)
|
|
255
|
+
'function_constructor_require', // new Function.constructor("require") (always malicious)
|
|
251
256
|
// Dangerous shell commands in dist/ are real threats, never bundler output
|
|
252
257
|
'dangerous_exec',
|
|
253
258
|
// Compound scoring rules — co-occurrence signals, never FP
|
|
@@ -394,6 +399,23 @@ const SCORING_COMPOUNDS = [
|
|
|
394
399
|
// Only obfuscation_detected + env_access must be in the same file (lifecycle_script is package-level)
|
|
395
400
|
sameFileTypes: ['obfuscation_detected', 'env_access']
|
|
396
401
|
},
|
|
402
|
+
// v2.10.89: Security review compounds
|
|
403
|
+
{
|
|
404
|
+
type: 'lifecycle_newsletter_hijack',
|
|
405
|
+
requires: ['lifecycle_script', 'newsletter_auto_follow'],
|
|
406
|
+
severity: 'CRITICAL',
|
|
407
|
+
message: 'Lifecycle hook + newsletter auto-follow — WhatsApp Baileys channel hijack via install-time hook (scoring compound).',
|
|
408
|
+
fileFrom: 'newsletter_auto_follow'
|
|
409
|
+
// No sameFile: lifecycle is package-level, newsletter_auto_follow is file-level
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
type: 'lifecycle_env_exfil',
|
|
413
|
+
requires: ['lifecycle_script', 'curl_env_exfil'],
|
|
414
|
+
severity: 'CRITICAL',
|
|
415
|
+
message: 'Lifecycle hook + curl/wget env exfiltration — install-time credential theft (scoring compound).',
|
|
416
|
+
fileFrom: 'curl_env_exfil'
|
|
417
|
+
// No sameFile: both are package-level
|
|
418
|
+
},
|
|
397
419
|
];
|
|
398
420
|
|
|
399
421
|
/**
|