mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-04-14 06:01:34 -07:00
Compare commits
5 Commits
995c371f48
...
f0abd500d9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f0abd500d9 | ||
|
|
8503cb86f1 | ||
|
|
5f0b670a82 | ||
|
|
9df814e351 | ||
|
|
88509ce8c2 |
3
.github/workflows/code_checks.yml
vendored
3
.github/workflows/code_checks.yml
vendored
@@ -21,7 +21,8 @@ jobs:
|
||||
run: |
|
||||
echo "🔍 Checking for incorrect absolute '/php/' URLs (should be 'php/' or './php/')..."
|
||||
|
||||
MATCHES=$(grep -rE "[\"']/\/php\/" --include=*.{js,php,html} ./front | grep -E "\.get|\.post|\.ajax|fetch|url\s*:") || true
|
||||
MATCHES=$(grep -rE "['\"]/php/" --include=\*.{js,php,html} ./front \
|
||||
| grep -E "\.get|\.post|\.ajax|fetch|url\s*:") || true
|
||||
|
||||
if [ -n "$MATCHES" ]; then
|
||||
echo "$MATCHES"
|
||||
|
||||
@@ -12,7 +12,7 @@ var timerRefreshData = ''
|
||||
|
||||
var emptyArr = ['undefined', "", undefined, null, 'null'];
|
||||
var UI_LANG = "English (en_us)";
|
||||
const allLanguages = ["ar_ar","ca_ca","cs_cz","de_de","en_us","es_es","fa_fa","fr_fr","it_it","nb_no","pl_pl","pt_br","pt_pt","ru_ru","sv_sv","tr_tr","uk_ua","zh_cn"]; // needs to be same as in lang.php
|
||||
const allLanguages = ["ar_ar","ca_ca","cs_cz","de_de","en_us","es_es","fa_fa","fr_fr","it_it","ja_jp","nb_no","pl_pl","pt_br","pt_pt","ru_ru","sv_sv","tr_tr","uk_ua","zh_cn"]; // needs to be same as in lang.php
|
||||
var settingsJSON = {}
|
||||
|
||||
|
||||
@@ -343,6 +343,9 @@ function getLangCode() {
|
||||
case 'Italian (it_it)':
|
||||
lang_code = 'it_it';
|
||||
break;
|
||||
case 'Japanese (ja_jp)':
|
||||
lang_code = 'ja_jp';
|
||||
break;
|
||||
case 'Russian (ru_ru)':
|
||||
lang_code = 'ru_ru';
|
||||
break;
|
||||
|
||||
@@ -761,4 +761,4 @@
|
||||
"settings_system_label": "تسمية النظام",
|
||||
"settings_update_item_warning": "تحذير تحديث العنصر",
|
||||
"test_event_tooltip": "تلميح اختبار الحدث"
|
||||
}
|
||||
}
|
||||
@@ -761,4 +761,4 @@
|
||||
"settings_system_label": "",
|
||||
"settings_update_item_warning": "",
|
||||
"test_event_tooltip": ""
|
||||
}
|
||||
}
|
||||
@@ -834,4 +834,4 @@
|
||||
"settings_system_label": "System",
|
||||
"settings_update_item_warning": "",
|
||||
"test_event_tooltip": "Speichere die Änderungen, bevor Sie die Einstellungen testen."
|
||||
}
|
||||
}
|
||||
@@ -761,4 +761,4 @@
|
||||
"settings_system_label": "",
|
||||
"settings_update_item_warning": "",
|
||||
"test_event_tooltip": ""
|
||||
}
|
||||
}
|
||||
764
front/php/templates/language/ja_jp.json
Normal file
764
front/php/templates/language/ja_jp.json
Normal file
@@ -0,0 +1,764 @@
|
||||
{
|
||||
"API_CUSTOM_SQL_description": "",
|
||||
"API_CUSTOM_SQL_name": "",
|
||||
"API_TOKEN_description": "",
|
||||
"API_TOKEN_name": "",
|
||||
"API_display_name": "",
|
||||
"API_icon": "",
|
||||
"About_Design": "",
|
||||
"About_Exit": "",
|
||||
"About_Title": "",
|
||||
"AppEvents_AppEventProcessed": "",
|
||||
"AppEvents_DateTimeCreated": "",
|
||||
"AppEvents_Extra": "",
|
||||
"AppEvents_GUID": "",
|
||||
"AppEvents_Helper1": "",
|
||||
"AppEvents_Helper2": "",
|
||||
"AppEvents_Helper3": "",
|
||||
"AppEvents_ObjectForeignKey": "",
|
||||
"AppEvents_ObjectIndex": "",
|
||||
"AppEvents_ObjectIsArchived": "",
|
||||
"AppEvents_ObjectIsNew": "",
|
||||
"AppEvents_ObjectPlugin": "",
|
||||
"AppEvents_ObjectPrimaryID": "",
|
||||
"AppEvents_ObjectSecondaryID": "",
|
||||
"AppEvents_ObjectStatus": "",
|
||||
"AppEvents_ObjectStatusColumn": "",
|
||||
"AppEvents_ObjectType": "",
|
||||
"AppEvents_Plugin": "",
|
||||
"AppEvents_Type": "",
|
||||
"BackDevDetail_Actions_Ask_Run": "",
|
||||
"BackDevDetail_Actions_Not_Registered": "",
|
||||
"BackDevDetail_Actions_Title_Run": "",
|
||||
"BackDevDetail_Copy_Ask": "",
|
||||
"BackDevDetail_Copy_Title": "",
|
||||
"BackDevDetail_Tools_WOL_error": "",
|
||||
"BackDevDetail_Tools_WOL_okay": "",
|
||||
"BackDevices_Arpscan_disabled": "",
|
||||
"BackDevices_Arpscan_enabled": "",
|
||||
"BackDevices_Backup_CopError": "",
|
||||
"BackDevices_Backup_Failed": "",
|
||||
"BackDevices_Backup_okay": "",
|
||||
"BackDevices_DBTools_DelDevError_a": "",
|
||||
"BackDevices_DBTools_DelDevError_b": "",
|
||||
"BackDevices_DBTools_DelDev_a": "",
|
||||
"BackDevices_DBTools_DelDev_b": "",
|
||||
"BackDevices_DBTools_DelEvents": "",
|
||||
"BackDevices_DBTools_DelEventsError": "",
|
||||
"BackDevices_DBTools_ImportCSV": "",
|
||||
"BackDevices_DBTools_ImportCSVError": "",
|
||||
"BackDevices_DBTools_ImportCSVMissing": "",
|
||||
"BackDevices_DBTools_Purge": "",
|
||||
"BackDevices_DBTools_UpdDev": "",
|
||||
"BackDevices_DBTools_UpdDevError": "",
|
||||
"BackDevices_DBTools_Upgrade": "",
|
||||
"BackDevices_DBTools_UpgradeError": "",
|
||||
"BackDevices_Device_UpdDevError": "",
|
||||
"BackDevices_Restore_CopError": "",
|
||||
"BackDevices_Restore_Failed": "",
|
||||
"BackDevices_Restore_okay": "",
|
||||
"BackDevices_darkmode_disabled": "",
|
||||
"BackDevices_darkmode_enabled": "",
|
||||
"CLEAR_NEW_FLAG_description": "",
|
||||
"CLEAR_NEW_FLAG_name": "",
|
||||
"CustProps_cant_remove": "",
|
||||
"DAYS_TO_KEEP_EVENTS_description": "",
|
||||
"DAYS_TO_KEEP_EVENTS_name": "",
|
||||
"DISCOVER_PLUGINS_description": "",
|
||||
"DISCOVER_PLUGINS_name": "",
|
||||
"DevDetail_Children_Title": "",
|
||||
"DevDetail_Copy_Device_Title": "",
|
||||
"DevDetail_Copy_Device_Tooltip": "",
|
||||
"DevDetail_CustomProperties_Title": "",
|
||||
"DevDetail_CustomProps_reset_info": "",
|
||||
"DevDetail_DisplayFields_Title": "",
|
||||
"DevDetail_EveandAl_AlertAllEvents": "",
|
||||
"DevDetail_EveandAl_AlertDown": "",
|
||||
"DevDetail_EveandAl_Archived": "",
|
||||
"DevDetail_EveandAl_NewDevice": "",
|
||||
"DevDetail_EveandAl_NewDevice_Tooltip": "",
|
||||
"DevDetail_EveandAl_RandomMAC": "",
|
||||
"DevDetail_EveandAl_ScanCycle": "",
|
||||
"DevDetail_EveandAl_ScanCycle_a": "",
|
||||
"DevDetail_EveandAl_ScanCycle_z": "",
|
||||
"DevDetail_EveandAl_Skip": "",
|
||||
"DevDetail_EveandAl_Title": "",
|
||||
"DevDetail_Events_CheckBox": "",
|
||||
"DevDetail_GoToNetworkNode": "",
|
||||
"DevDetail_Icon": "",
|
||||
"DevDetail_Icon_Descr": "",
|
||||
"DevDetail_Loading": "",
|
||||
"DevDetail_MainInfo_Comments": "",
|
||||
"DevDetail_MainInfo_Favorite": "",
|
||||
"DevDetail_MainInfo_Group": "",
|
||||
"DevDetail_MainInfo_Location": "",
|
||||
"DevDetail_MainInfo_Name": "",
|
||||
"DevDetail_MainInfo_Network": "",
|
||||
"DevDetail_MainInfo_Network_Port": "",
|
||||
"DevDetail_MainInfo_Network_Site": "",
|
||||
"DevDetail_MainInfo_Network_Title": "",
|
||||
"DevDetail_MainInfo_Owner": "",
|
||||
"DevDetail_MainInfo_SSID": "",
|
||||
"DevDetail_MainInfo_Title": "",
|
||||
"DevDetail_MainInfo_Type": "",
|
||||
"DevDetail_MainInfo_Vendor": "",
|
||||
"DevDetail_MainInfo_mac": "",
|
||||
"DevDetail_NavToChildNode": "",
|
||||
"DevDetail_Network_Node_hover": "",
|
||||
"DevDetail_Network_Port_hover": "",
|
||||
"DevDetail_Nmap_Scans": "",
|
||||
"DevDetail_Nmap_Scans_desc": "",
|
||||
"DevDetail_Nmap_buttonDefault": "",
|
||||
"DevDetail_Nmap_buttonDefault_text": "",
|
||||
"DevDetail_Nmap_buttonDetail": "",
|
||||
"DevDetail_Nmap_buttonDetail_text": "",
|
||||
"DevDetail_Nmap_buttonFast": "",
|
||||
"DevDetail_Nmap_buttonFast_text": "",
|
||||
"DevDetail_Nmap_buttonSkipDiscovery": "",
|
||||
"DevDetail_Nmap_buttonSkipDiscovery_text": "",
|
||||
"DevDetail_Nmap_resultsLink": "",
|
||||
"DevDetail_Owner_hover": "",
|
||||
"DevDetail_Periodselect_All": "",
|
||||
"DevDetail_Periodselect_LastMonth": "",
|
||||
"DevDetail_Periodselect_LastWeek": "",
|
||||
"DevDetail_Periodselect_LastYear": "",
|
||||
"DevDetail_Periodselect_today": "",
|
||||
"DevDetail_Run_Actions_Title": "",
|
||||
"DevDetail_Run_Actions_Tooltip": "",
|
||||
"DevDetail_SessionInfo_FirstSession": "",
|
||||
"DevDetail_SessionInfo_LastIP": "",
|
||||
"DevDetail_SessionInfo_LastSession": "",
|
||||
"DevDetail_SessionInfo_StaticIP": "",
|
||||
"DevDetail_SessionInfo_Status": "",
|
||||
"DevDetail_SessionInfo_Title": "",
|
||||
"DevDetail_SessionTable_Additionalinfo": "",
|
||||
"DevDetail_SessionTable_Connection": "",
|
||||
"DevDetail_SessionTable_Disconnection": "",
|
||||
"DevDetail_SessionTable_Duration": "",
|
||||
"DevDetail_SessionTable_IP": "",
|
||||
"DevDetail_SessionTable_Order": "",
|
||||
"DevDetail_Shortcut_CurrentStatus": "",
|
||||
"DevDetail_Shortcut_DownAlerts": "",
|
||||
"DevDetail_Shortcut_Presence": "",
|
||||
"DevDetail_Shortcut_Sessions": "",
|
||||
"DevDetail_Tab_Details": "",
|
||||
"DevDetail_Tab_Events": "",
|
||||
"DevDetail_Tab_EventsTableDate": "",
|
||||
"DevDetail_Tab_EventsTableEvent": "",
|
||||
"DevDetail_Tab_EventsTableIP": "",
|
||||
"DevDetail_Tab_EventsTableInfo": "",
|
||||
"DevDetail_Tab_Nmap": "",
|
||||
"DevDetail_Tab_NmapEmpty": "",
|
||||
"DevDetail_Tab_NmapTableExtra": "",
|
||||
"DevDetail_Tab_NmapTableHeader": "",
|
||||
"DevDetail_Tab_NmapTableIndex": "",
|
||||
"DevDetail_Tab_NmapTablePort": "",
|
||||
"DevDetail_Tab_NmapTableService": "",
|
||||
"DevDetail_Tab_NmapTableState": "",
|
||||
"DevDetail_Tab_NmapTableText": "",
|
||||
"DevDetail_Tab_NmapTableTime": "",
|
||||
"DevDetail_Tab_Plugins": "",
|
||||
"DevDetail_Tab_Presence": "",
|
||||
"DevDetail_Tab_Sessions": "",
|
||||
"DevDetail_Tab_Tools": "",
|
||||
"DevDetail_Tab_Tools_Internet_Info_Description": "",
|
||||
"DevDetail_Tab_Tools_Internet_Info_Error": "",
|
||||
"DevDetail_Tab_Tools_Internet_Info_Start": "",
|
||||
"DevDetail_Tab_Tools_Internet_Info_Title": "",
|
||||
"DevDetail_Tab_Tools_Nslookup_Description": "",
|
||||
"DevDetail_Tab_Tools_Nslookup_Error": "",
|
||||
"DevDetail_Tab_Tools_Nslookup_Start": "",
|
||||
"DevDetail_Tab_Tools_Nslookup_Title": "",
|
||||
"DevDetail_Tab_Tools_Speedtest_Description": "",
|
||||
"DevDetail_Tab_Tools_Speedtest_Start": "",
|
||||
"DevDetail_Tab_Tools_Speedtest_Title": "",
|
||||
"DevDetail_Tab_Tools_Traceroute_Description": "",
|
||||
"DevDetail_Tab_Tools_Traceroute_Error": "",
|
||||
"DevDetail_Tab_Tools_Traceroute_Start": "",
|
||||
"DevDetail_Tab_Tools_Traceroute_Title": "",
|
||||
"DevDetail_Tools_WOL": "",
|
||||
"DevDetail_Tools_WOL_noti": "",
|
||||
"DevDetail_Tools_WOL_noti_text": "",
|
||||
"DevDetail_Type_hover": "",
|
||||
"DevDetail_Vendor_hover": "",
|
||||
"DevDetail_WOL_Title": "",
|
||||
"DevDetail_button_AddIcon": "",
|
||||
"DevDetail_button_AddIcon_Help": "",
|
||||
"DevDetail_button_AddIcon_Tooltip": "",
|
||||
"DevDetail_button_Delete": "",
|
||||
"DevDetail_button_DeleteEvents": "",
|
||||
"DevDetail_button_DeleteEvents_Warning": "",
|
||||
"DevDetail_button_Delete_ask": "",
|
||||
"DevDetail_button_OverwriteIcons": "",
|
||||
"DevDetail_button_OverwriteIcons_Tooltip": "",
|
||||
"DevDetail_button_OverwriteIcons_Warning": "",
|
||||
"DevDetail_button_Reset": "",
|
||||
"DevDetail_button_Save": "",
|
||||
"DeviceEdit_ValidMacIp": "",
|
||||
"Device_MultiEdit": "",
|
||||
"Device_MultiEdit_Backup": "",
|
||||
"Device_MultiEdit_Fields": "",
|
||||
"Device_MultiEdit_MassActions": "",
|
||||
"Device_MultiEdit_No_Devices": "",
|
||||
"Device_MultiEdit_Tooltip": "",
|
||||
"Device_Searchbox": "",
|
||||
"Device_Shortcut_AllDevices": "",
|
||||
"Device_Shortcut_AllNodes": "",
|
||||
"Device_Shortcut_Archived": "",
|
||||
"Device_Shortcut_Connected": "",
|
||||
"Device_Shortcut_Devices": "",
|
||||
"Device_Shortcut_DownAlerts": "",
|
||||
"Device_Shortcut_DownOnly": "",
|
||||
"Device_Shortcut_Favorites": "",
|
||||
"Device_Shortcut_NewDevices": "",
|
||||
"Device_Shortcut_OnlineChart": "",
|
||||
"Device_TableHead_AlertDown": "",
|
||||
"Device_TableHead_Connected_Devices": "",
|
||||
"Device_TableHead_CustomProps": "",
|
||||
"Device_TableHead_FQDN": "",
|
||||
"Device_TableHead_Favorite": "",
|
||||
"Device_TableHead_FirstSession": "",
|
||||
"Device_TableHead_GUID": "",
|
||||
"Device_TableHead_Group": "",
|
||||
"Device_TableHead_Icon": "",
|
||||
"Device_TableHead_LastIP": "",
|
||||
"Device_TableHead_LastIPOrder": "",
|
||||
"Device_TableHead_LastSession": "",
|
||||
"Device_TableHead_Location": "",
|
||||
"Device_TableHead_MAC": "",
|
||||
"Device_TableHead_MAC_full": "",
|
||||
"Device_TableHead_Name": "",
|
||||
"Device_TableHead_NetworkSite": "",
|
||||
"Device_TableHead_Owner": "",
|
||||
"Device_TableHead_ParentRelType": "",
|
||||
"Device_TableHead_Parent_MAC": "",
|
||||
"Device_TableHead_Port": "",
|
||||
"Device_TableHead_PresentLastScan": "",
|
||||
"Device_TableHead_ReqNicsOnline": "",
|
||||
"Device_TableHead_RowID": "",
|
||||
"Device_TableHead_Rowid": "",
|
||||
"Device_TableHead_SSID": "",
|
||||
"Device_TableHead_SourcePlugin": "",
|
||||
"Device_TableHead_Status": "",
|
||||
"Device_TableHead_SyncHubNodeName": "",
|
||||
"Device_TableHead_Type": "",
|
||||
"Device_TableHead_Vendor": "",
|
||||
"Device_Table_Not_Network_Device": "",
|
||||
"Device_Table_info": "",
|
||||
"Device_Table_nav_next": "",
|
||||
"Device_Table_nav_prev": "",
|
||||
"Device_Tablelenght": "",
|
||||
"Device_Tablelenght_all": "",
|
||||
"Device_Title": "",
|
||||
"Devices_Filters": "",
|
||||
"ENABLE_PLUGINS_description": "",
|
||||
"ENABLE_PLUGINS_name": "",
|
||||
"ENCRYPTION_KEY_description": "",
|
||||
"ENCRYPTION_KEY_name": "",
|
||||
"Email_display_name": "",
|
||||
"Email_icon": "",
|
||||
"Events_Loading": "",
|
||||
"Events_Periodselect_All": "",
|
||||
"Events_Periodselect_LastMonth": "",
|
||||
"Events_Periodselect_LastWeek": "",
|
||||
"Events_Periodselect_LastYear": "",
|
||||
"Events_Periodselect_today": "",
|
||||
"Events_Searchbox": "",
|
||||
"Events_Shortcut_AllEvents": "",
|
||||
"Events_Shortcut_DownAlerts": "",
|
||||
"Events_Shortcut_Events": "",
|
||||
"Events_Shortcut_MissSessions": "",
|
||||
"Events_Shortcut_NewDevices": "",
|
||||
"Events_Shortcut_Sessions": "",
|
||||
"Events_Shortcut_VoidSessions": "",
|
||||
"Events_TableHead_AdditionalInfo": "",
|
||||
"Events_TableHead_Connection": "",
|
||||
"Events_TableHead_Date": "",
|
||||
"Events_TableHead_Device": "",
|
||||
"Events_TableHead_Disconnection": "",
|
||||
"Events_TableHead_Duration": "",
|
||||
"Events_TableHead_DurationOrder": "",
|
||||
"Events_TableHead_EventType": "",
|
||||
"Events_TableHead_IP": "",
|
||||
"Events_TableHead_IPOrder": "",
|
||||
"Events_TableHead_Order": "",
|
||||
"Events_TableHead_Owner": "",
|
||||
"Events_TableHead_PendingAlert": "",
|
||||
"Events_Table_info": "",
|
||||
"Events_Table_nav_next": "",
|
||||
"Events_Table_nav_prev": "",
|
||||
"Events_Tablelenght": "",
|
||||
"Events_Tablelenght_all": "",
|
||||
"Events_Title": "",
|
||||
"GRAPHQL_PORT_description": "",
|
||||
"GRAPHQL_PORT_name": "",
|
||||
"Gen_Action": "",
|
||||
"Gen_Add": "",
|
||||
"Gen_AddDevice": "",
|
||||
"Gen_Add_All": "",
|
||||
"Gen_All_Devices": "",
|
||||
"Gen_AreYouSure": "",
|
||||
"Gen_Backup": "",
|
||||
"Gen_Cancel": "",
|
||||
"Gen_Change": "",
|
||||
"Gen_Copy": "",
|
||||
"Gen_CopyToClipboard": "",
|
||||
"Gen_DataUpdatedUITakesTime": "",
|
||||
"Gen_Delete": "",
|
||||
"Gen_DeleteAll": "",
|
||||
"Gen_Description": "",
|
||||
"Gen_Error": "",
|
||||
"Gen_Filter": "",
|
||||
"Gen_Generate": "",
|
||||
"Gen_InvalidMac": "",
|
||||
"Gen_LockedDB": "",
|
||||
"Gen_NetworkMask": "",
|
||||
"Gen_Offline": "",
|
||||
"Gen_Okay": "",
|
||||
"Gen_Online": "",
|
||||
"Gen_Purge": "",
|
||||
"Gen_ReadDocs": "",
|
||||
"Gen_Remove_All": "",
|
||||
"Gen_Remove_Last": "",
|
||||
"Gen_Reset": "",
|
||||
"Gen_Restore": "",
|
||||
"Gen_Run": "",
|
||||
"Gen_Save": "",
|
||||
"Gen_Saved": "",
|
||||
"Gen_Search": "",
|
||||
"Gen_Select": "",
|
||||
"Gen_SelectIcon": "",
|
||||
"Gen_SelectToPreview": "",
|
||||
"Gen_Selected_Devices": "",
|
||||
"Gen_Subnet": "",
|
||||
"Gen_Switch": "",
|
||||
"Gen_Upd": "",
|
||||
"Gen_Upd_Fail": "",
|
||||
"Gen_Update": "",
|
||||
"Gen_Update_Value": "",
|
||||
"Gen_ValidIcon": "",
|
||||
"Gen_Warning": "",
|
||||
"Gen_Work_In_Progress": "",
|
||||
"Gen_create_new_device": "",
|
||||
"Gen_create_new_device_info": "",
|
||||
"General_display_name": "",
|
||||
"General_icon": "",
|
||||
"HRS_TO_KEEP_NEWDEV_description": "",
|
||||
"HRS_TO_KEEP_NEWDEV_name": "",
|
||||
"HRS_TO_KEEP_OFFDEV_description": "",
|
||||
"HRS_TO_KEEP_OFFDEV_name": "",
|
||||
"LOADED_PLUGINS_description": "",
|
||||
"LOADED_PLUGINS_name": "",
|
||||
"LOG_LEVEL_description": "",
|
||||
"LOG_LEVEL_name": "",
|
||||
"Loading": "",
|
||||
"Login_Box": "",
|
||||
"Login_Default_PWD": "",
|
||||
"Login_Info": "",
|
||||
"Login_Psw-box": "",
|
||||
"Login_Psw_alert": "",
|
||||
"Login_Psw_folder": "",
|
||||
"Login_Psw_new": "",
|
||||
"Login_Psw_run": "",
|
||||
"Login_Remember": "",
|
||||
"Login_Remember_small": "",
|
||||
"Login_Submit": "",
|
||||
"Login_Toggle_Alert_headline": "",
|
||||
"Login_Toggle_Info": "",
|
||||
"Login_Toggle_Info_headline": "",
|
||||
"Maint_PurgeLog": "",
|
||||
"Maint_RestartServer": "",
|
||||
"Maint_Restart_Server_noti_text": "",
|
||||
"Maintenance_InitCheck": "",
|
||||
"Maintenance_InitCheck_Checking": "",
|
||||
"Maintenance_InitCheck_QuickSetupGuide": "",
|
||||
"Maintenance_InitCheck_Success": "",
|
||||
"Maintenance_ReCheck": "",
|
||||
"Maintenance_Running_Version": "",
|
||||
"Maintenance_Status": "",
|
||||
"Maintenance_Title": "",
|
||||
"Maintenance_Tool_DownloadConfig": "",
|
||||
"Maintenance_Tool_DownloadConfig_text": "",
|
||||
"Maintenance_Tool_DownloadWorkflows": "",
|
||||
"Maintenance_Tool_DownloadWorkflows_text": "",
|
||||
"Maintenance_Tool_ExportCSV": "",
|
||||
"Maintenance_Tool_ExportCSV_noti": "",
|
||||
"Maintenance_Tool_ExportCSV_noti_text": "",
|
||||
"Maintenance_Tool_ExportCSV_text": "",
|
||||
"Maintenance_Tool_ImportCSV": "",
|
||||
"Maintenance_Tool_ImportCSV_noti": "",
|
||||
"Maintenance_Tool_ImportCSV_noti_text": "",
|
||||
"Maintenance_Tool_ImportCSV_text": "",
|
||||
"Maintenance_Tool_ImportConfig_noti": "",
|
||||
"Maintenance_Tool_ImportPastedCSV": "",
|
||||
"Maintenance_Tool_ImportPastedCSV_noti_text": "",
|
||||
"Maintenance_Tool_ImportPastedCSV_text": "",
|
||||
"Maintenance_Tool_ImportPastedConfig": "",
|
||||
"Maintenance_Tool_ImportPastedConfig_noti_text": "",
|
||||
"Maintenance_Tool_ImportPastedConfig_text": "",
|
||||
"Maintenance_Tool_arpscansw": "",
|
||||
"Maintenance_Tool_arpscansw_noti": "",
|
||||
"Maintenance_Tool_arpscansw_noti_text": "",
|
||||
"Maintenance_Tool_arpscansw_text": "",
|
||||
"Maintenance_Tool_backup": "",
|
||||
"Maintenance_Tool_backup_noti": "",
|
||||
"Maintenance_Tool_backup_noti_text": "",
|
||||
"Maintenance_Tool_backup_text": "",
|
||||
"Maintenance_Tool_check_visible": "",
|
||||
"Maintenance_Tool_darkmode": "",
|
||||
"Maintenance_Tool_darkmode_noti": "",
|
||||
"Maintenance_Tool_darkmode_noti_text": "",
|
||||
"Maintenance_Tool_darkmode_text": "",
|
||||
"Maintenance_Tool_del_ActHistory": "",
|
||||
"Maintenance_Tool_del_ActHistory_noti": "",
|
||||
"Maintenance_Tool_del_ActHistory_noti_text": "",
|
||||
"Maintenance_Tool_del_ActHistory_text": "",
|
||||
"Maintenance_Tool_del_alldev": "",
|
||||
"Maintenance_Tool_del_alldev_noti": "",
|
||||
"Maintenance_Tool_del_alldev_noti_text": "",
|
||||
"Maintenance_Tool_del_alldev_text": "",
|
||||
"Maintenance_Tool_del_allevents": "",
|
||||
"Maintenance_Tool_del_allevents30": "",
|
||||
"Maintenance_Tool_del_allevents30_noti": "",
|
||||
"Maintenance_Tool_del_allevents30_noti_text": "",
|
||||
"Maintenance_Tool_del_allevents30_text": "",
|
||||
"Maintenance_Tool_del_allevents_noti": "",
|
||||
"Maintenance_Tool_del_allevents_noti_text": "",
|
||||
"Maintenance_Tool_del_allevents_text": "",
|
||||
"Maintenance_Tool_del_empty_macs": "",
|
||||
"Maintenance_Tool_del_empty_macs_noti": "",
|
||||
"Maintenance_Tool_del_empty_macs_noti_text": "",
|
||||
"Maintenance_Tool_del_empty_macs_text": "",
|
||||
"Maintenance_Tool_del_selecteddev": "",
|
||||
"Maintenance_Tool_del_selecteddev_text": "",
|
||||
"Maintenance_Tool_del_unknowndev": "",
|
||||
"Maintenance_Tool_del_unknowndev_noti": "",
|
||||
"Maintenance_Tool_del_unknowndev_noti_text": "",
|
||||
"Maintenance_Tool_del_unknowndev_text": "",
|
||||
"Maintenance_Tool_displayed_columns_text": "",
|
||||
"Maintenance_Tool_drag_me": "",
|
||||
"Maintenance_Tool_order_columns_text": "",
|
||||
"Maintenance_Tool_purgebackup": "",
|
||||
"Maintenance_Tool_purgebackup_noti": "",
|
||||
"Maintenance_Tool_purgebackup_noti_text": "",
|
||||
"Maintenance_Tool_purgebackup_text": "",
|
||||
"Maintenance_Tool_restore": "",
|
||||
"Maintenance_Tool_restore_noti": "",
|
||||
"Maintenance_Tool_restore_noti_text": "",
|
||||
"Maintenance_Tool_restore_text": "",
|
||||
"Maintenance_Tool_upgrade_database_noti": "",
|
||||
"Maintenance_Tool_upgrade_database_noti_text": "",
|
||||
"Maintenance_Tool_upgrade_database_text": "",
|
||||
"Maintenance_Tools_Tab_BackupRestore": "",
|
||||
"Maintenance_Tools_Tab_Logging": "",
|
||||
"Maintenance_Tools_Tab_Settings": "",
|
||||
"Maintenance_Tools_Tab_Tools": "",
|
||||
"Maintenance_Tools_Tab_UISettings": "",
|
||||
"Maintenance_arp_status": "",
|
||||
"Maintenance_arp_status_off": "",
|
||||
"Maintenance_arp_status_on": "",
|
||||
"Maintenance_built_on": "",
|
||||
"Maintenance_current_version": "",
|
||||
"Maintenance_database_backup": "",
|
||||
"Maintenance_database_backup_found": "",
|
||||
"Maintenance_database_backup_total": "",
|
||||
"Maintenance_database_lastmod": "",
|
||||
"Maintenance_database_path": "",
|
||||
"Maintenance_database_rows": "",
|
||||
"Maintenance_database_size": "",
|
||||
"Maintenance_lang_selector_apply": "",
|
||||
"Maintenance_lang_selector_empty": "",
|
||||
"Maintenance_lang_selector_lable": "",
|
||||
"Maintenance_lang_selector_text": "",
|
||||
"Maintenance_new_version": "",
|
||||
"Maintenance_themeselector_apply": "",
|
||||
"Maintenance_themeselector_empty": "",
|
||||
"Maintenance_themeselector_lable": "",
|
||||
"Maintenance_themeselector_text": "",
|
||||
"Maintenance_version": "",
|
||||
"NETWORK_DEVICE_TYPES_description": "",
|
||||
"NETWORK_DEVICE_TYPES_name": "",
|
||||
"Navigation_About": "",
|
||||
"Navigation_AppEvents": "",
|
||||
"Navigation_Devices": "",
|
||||
"Navigation_Donations": "",
|
||||
"Navigation_Events": "",
|
||||
"Navigation_Integrations": "",
|
||||
"Navigation_Maintenance": "",
|
||||
"Navigation_Monitoring": "",
|
||||
"Navigation_Network": "",
|
||||
"Navigation_Notifications": "",
|
||||
"Navigation_Plugins": "",
|
||||
"Navigation_Presence": "",
|
||||
"Navigation_Report": "",
|
||||
"Navigation_Settings": "",
|
||||
"Navigation_SystemInfo": "",
|
||||
"Navigation_Workflows": "",
|
||||
"Network_Assign": "",
|
||||
"Network_Cant_Assign": "",
|
||||
"Network_Cant_Assign_No_Node_Selected": "",
|
||||
"Network_Configuration_Error": "",
|
||||
"Network_Connected": "",
|
||||
"Network_Devices": "",
|
||||
"Network_ManageAdd": "",
|
||||
"Network_ManageAdd_Name": "",
|
||||
"Network_ManageAdd_Name_text": "",
|
||||
"Network_ManageAdd_Port": "",
|
||||
"Network_ManageAdd_Port_text": "",
|
||||
"Network_ManageAdd_Submit": "",
|
||||
"Network_ManageAdd_Type": "",
|
||||
"Network_ManageAdd_Type_text": "",
|
||||
"Network_ManageAssign": "",
|
||||
"Network_ManageDel": "",
|
||||
"Network_ManageDel_Name": "",
|
||||
"Network_ManageDel_Name_text": "",
|
||||
"Network_ManageDel_Submit": "",
|
||||
"Network_ManageDevices": "",
|
||||
"Network_ManageEdit": "",
|
||||
"Network_ManageEdit_ID": "",
|
||||
"Network_ManageEdit_ID_text": "",
|
||||
"Network_ManageEdit_Name": "",
|
||||
"Network_ManageEdit_Name_text": "",
|
||||
"Network_ManageEdit_Port": "",
|
||||
"Network_ManageEdit_Port_text": "",
|
||||
"Network_ManageEdit_Submit": "",
|
||||
"Network_ManageEdit_Type": "",
|
||||
"Network_ManageEdit_Type_text": "",
|
||||
"Network_ManageLeaf": "",
|
||||
"Network_ManageUnassign": "",
|
||||
"Network_NoAssignedDevices": "",
|
||||
"Network_NoDevices": "",
|
||||
"Network_Node": "",
|
||||
"Network_Node_Name": "",
|
||||
"Network_Parent": "",
|
||||
"Network_Root": "",
|
||||
"Network_Root_Not_Configured": "",
|
||||
"Network_Root_Unconfigurable": "",
|
||||
"Network_ShowArchived": "",
|
||||
"Network_ShowOffline": "",
|
||||
"Network_Table_Hostname": "",
|
||||
"Network_Table_IP": "",
|
||||
"Network_Table_State": "",
|
||||
"Network_Title": "",
|
||||
"Network_UnassignedDevices": "",
|
||||
"Notifications_All": "",
|
||||
"Notifications_Mark_All_Read": "",
|
||||
"PIALERT_WEB_PASSWORD_description": "",
|
||||
"PIALERT_WEB_PASSWORD_name": "",
|
||||
"PIALERT_WEB_PROTECTION_description": "",
|
||||
"PIALERT_WEB_PROTECTION_name": "",
|
||||
"PLUGINS_KEEP_HIST_description": "",
|
||||
"PLUGINS_KEEP_HIST_name": "",
|
||||
"Plugins_DeleteAll": "",
|
||||
"Plugins_Filters_Mac": "",
|
||||
"Plugins_History": "",
|
||||
"Plugins_Obj_DeleteListed": "",
|
||||
"Plugins_Objects": "",
|
||||
"Plugins_Out_of": "",
|
||||
"Plugins_Unprocessed_Events": "",
|
||||
"Plugins_no_control": "",
|
||||
"Presence_CalHead_day": "",
|
||||
"Presence_CalHead_lang": "",
|
||||
"Presence_CalHead_month": "",
|
||||
"Presence_CalHead_quarter": "",
|
||||
"Presence_CalHead_week": "",
|
||||
"Presence_CalHead_year": "",
|
||||
"Presence_CallHead_Devices": "",
|
||||
"Presence_Key_OnlineNow": "",
|
||||
"Presence_Key_OnlineNow_desc": "",
|
||||
"Presence_Key_OnlinePast": "",
|
||||
"Presence_Key_OnlinePastMiss": "",
|
||||
"Presence_Key_OnlinePastMiss_desc": "",
|
||||
"Presence_Key_OnlinePast_desc": "",
|
||||
"Presence_Loading": "",
|
||||
"Presence_Shortcut_AllDevices": "",
|
||||
"Presence_Shortcut_Archived": "",
|
||||
"Presence_Shortcut_Connected": "",
|
||||
"Presence_Shortcut_Devices": "",
|
||||
"Presence_Shortcut_DownAlerts": "",
|
||||
"Presence_Shortcut_Favorites": "",
|
||||
"Presence_Shortcut_NewDevices": "",
|
||||
"Presence_Title": "",
|
||||
"REFRESH_FQDN_description": "",
|
||||
"REFRESH_FQDN_name": "",
|
||||
"REPORT_DASHBOARD_URL_description": "",
|
||||
"REPORT_DASHBOARD_URL_name": "",
|
||||
"REPORT_ERROR": "",
|
||||
"REPORT_MAIL_description": "",
|
||||
"REPORT_MAIL_name": "",
|
||||
"REPORT_TITLE": "",
|
||||
"RandomMAC_hover": "",
|
||||
"Reports_Sent_Log": "",
|
||||
"SCAN_SUBNETS_description": "",
|
||||
"SCAN_SUBNETS_name": "",
|
||||
"SYSTEM_TITLE": "",
|
||||
"Setting_Override": "",
|
||||
"Setting_Override_Description": "",
|
||||
"Settings_Metadata_Toggle": "",
|
||||
"Settings_Show_Description": "",
|
||||
"Settings_device_Scanners_desync": "",
|
||||
"Settings_device_Scanners_desync_popup": "",
|
||||
"Speedtest_Results": "",
|
||||
"Systeminfo_AvailableIps": "",
|
||||
"Systeminfo_CPU": "",
|
||||
"Systeminfo_CPU_Cores": "",
|
||||
"Systeminfo_CPU_Name": "",
|
||||
"Systeminfo_CPU_Speed": "",
|
||||
"Systeminfo_CPU_Temp": "",
|
||||
"Systeminfo_CPU_Vendor": "",
|
||||
"Systeminfo_Client_Resolution": "",
|
||||
"Systeminfo_Client_User_Agent": "",
|
||||
"Systeminfo_General": "",
|
||||
"Systeminfo_General_Date": "",
|
||||
"Systeminfo_General_Date2": "",
|
||||
"Systeminfo_General_Full_Date": "",
|
||||
"Systeminfo_General_TimeZone": "",
|
||||
"Systeminfo_Memory": "",
|
||||
"Systeminfo_Memory_Total_Memory": "",
|
||||
"Systeminfo_Memory_Usage": "",
|
||||
"Systeminfo_Memory_Usage_Percent": "",
|
||||
"Systeminfo_Motherboard": "",
|
||||
"Systeminfo_Motherboard_BIOS": "",
|
||||
"Systeminfo_Motherboard_BIOS_Date": "",
|
||||
"Systeminfo_Motherboard_BIOS_Vendor": "",
|
||||
"Systeminfo_Motherboard_Manufactured": "",
|
||||
"Systeminfo_Motherboard_Name": "",
|
||||
"Systeminfo_Motherboard_Revision": "",
|
||||
"Systeminfo_Network": "",
|
||||
"Systeminfo_Network_Accept_Encoding": "",
|
||||
"Systeminfo_Network_Accept_Language": "",
|
||||
"Systeminfo_Network_Connection_Port": "",
|
||||
"Systeminfo_Network_HTTP_Host": "",
|
||||
"Systeminfo_Network_HTTP_Referer": "",
|
||||
"Systeminfo_Network_HTTP_Referer_String": "",
|
||||
"Systeminfo_Network_Hardware": "",
|
||||
"Systeminfo_Network_Hardware_Interface_Mask": "",
|
||||
"Systeminfo_Network_Hardware_Interface_Name": "",
|
||||
"Systeminfo_Network_Hardware_Interface_RX": "",
|
||||
"Systeminfo_Network_Hardware_Interface_TX": "",
|
||||
"Systeminfo_Network_IP": "",
|
||||
"Systeminfo_Network_IP_Connection": "",
|
||||
"Systeminfo_Network_IP_Server": "",
|
||||
"Systeminfo_Network_MIME": "",
|
||||
"Systeminfo_Network_Request_Method": "",
|
||||
"Systeminfo_Network_Request_Time": "",
|
||||
"Systeminfo_Network_Request_URI": "",
|
||||
"Systeminfo_Network_Secure_Connection": "",
|
||||
"Systeminfo_Network_Secure_Connection_String": "",
|
||||
"Systeminfo_Network_Server_Name": "",
|
||||
"Systeminfo_Network_Server_Name_String": "",
|
||||
"Systeminfo_Network_Server_Query": "",
|
||||
"Systeminfo_Network_Server_Query_String": "",
|
||||
"Systeminfo_Network_Server_Version": "",
|
||||
"Systeminfo_Services": "",
|
||||
"Systeminfo_Services_Description": "",
|
||||
"Systeminfo_Services_Name": "",
|
||||
"Systeminfo_Storage": "",
|
||||
"Systeminfo_Storage_Device": "",
|
||||
"Systeminfo_Storage_Mount": "",
|
||||
"Systeminfo_Storage_Size": "",
|
||||
"Systeminfo_Storage_Type": "",
|
||||
"Systeminfo_Storage_Usage": "",
|
||||
"Systeminfo_Storage_Usage_Free": "",
|
||||
"Systeminfo_Storage_Usage_Mount": "",
|
||||
"Systeminfo_Storage_Usage_Total": "",
|
||||
"Systeminfo_Storage_Usage_Used": "",
|
||||
"Systeminfo_System": "",
|
||||
"Systeminfo_System_AVG": "",
|
||||
"Systeminfo_System_Architecture": "",
|
||||
"Systeminfo_System_Kernel": "",
|
||||
"Systeminfo_System_OSVersion": "",
|
||||
"Systeminfo_System_Running_Processes": "",
|
||||
"Systeminfo_System_System": "",
|
||||
"Systeminfo_System_Uname": "",
|
||||
"Systeminfo_System_Uptime": "",
|
||||
"Systeminfo_This_Client": "",
|
||||
"Systeminfo_USB_Devices": "",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "",
|
||||
"TIMEZONE_description": "",
|
||||
"TIMEZONE_name": "",
|
||||
"UI_DEV_SECTIONS_description": "",
|
||||
"UI_DEV_SECTIONS_name": "",
|
||||
"UI_ICONS_description": "",
|
||||
"UI_ICONS_name": "",
|
||||
"UI_LANG_description": "",
|
||||
"UI_LANG_name": "",
|
||||
"UI_MY_DEVICES_description": "",
|
||||
"UI_MY_DEVICES_name": "",
|
||||
"UI_NOT_RANDOM_MAC_description": "",
|
||||
"UI_NOT_RANDOM_MAC_name": "",
|
||||
"UI_PRESENCE_description": "",
|
||||
"UI_PRESENCE_name": "",
|
||||
"UI_REFRESH_description": "",
|
||||
"UI_REFRESH_name": "",
|
||||
"VERSION_description": "",
|
||||
"VERSION_name": "",
|
||||
"WF_Action_Add": "",
|
||||
"WF_Action_field": "",
|
||||
"WF_Action_type": "",
|
||||
"WF_Action_value": "",
|
||||
"WF_Actions": "",
|
||||
"WF_Add": "",
|
||||
"WF_Add_Condition": "",
|
||||
"WF_Add_Group": "",
|
||||
"WF_Condition_field": "",
|
||||
"WF_Condition_operator": "",
|
||||
"WF_Condition_value": "",
|
||||
"WF_Conditions": "",
|
||||
"WF_Conditions_logic_rules": "",
|
||||
"WF_Duplicate": "",
|
||||
"WF_Enabled": "",
|
||||
"WF_Export": "",
|
||||
"WF_Export_Copy": "",
|
||||
"WF_Import": "",
|
||||
"WF_Import_Copy": "",
|
||||
"WF_Name": "",
|
||||
"WF_Remove": "",
|
||||
"WF_Remove_Copy": "",
|
||||
"WF_Save": "",
|
||||
"WF_Trigger": "",
|
||||
"WF_Trigger_event_type": "",
|
||||
"WF_Trigger_type": "",
|
||||
"add_icon_event_tooltip": "",
|
||||
"add_option_event_tooltip": "",
|
||||
"copy_icons_event_tooltip": "",
|
||||
"devices_old": "",
|
||||
"general_event_description": "",
|
||||
"general_event_title": "",
|
||||
"go_to_device_event_tooltip": "",
|
||||
"go_to_node_event_tooltip": "",
|
||||
"new_version_available": "",
|
||||
"report_guid": "",
|
||||
"report_guid_missing": "",
|
||||
"report_select_format": "",
|
||||
"report_time": "",
|
||||
"run_event_tooltip": "",
|
||||
"select_icon_event_tooltip": "",
|
||||
"settings_core_icon": "",
|
||||
"settings_core_label": "",
|
||||
"settings_device_scanners": "",
|
||||
"settings_device_scanners_icon": "",
|
||||
"settings_device_scanners_info": "",
|
||||
"settings_device_scanners_label": "",
|
||||
"settings_enabled": "",
|
||||
"settings_enabled_icon": "",
|
||||
"settings_expand_all": "",
|
||||
"settings_imported": "",
|
||||
"settings_imported_label": "",
|
||||
"settings_missing": "",
|
||||
"settings_missing_block": "",
|
||||
"settings_old": "",
|
||||
"settings_other_scanners": "",
|
||||
"settings_other_scanners_icon": "",
|
||||
"settings_other_scanners_label": "",
|
||||
"settings_publishers": "",
|
||||
"settings_publishers_icon": "",
|
||||
"settings_publishers_info": "",
|
||||
"settings_publishers_label": "",
|
||||
"settings_readonly": "",
|
||||
"settings_saved": "",
|
||||
"settings_system_icon": "",
|
||||
"settings_system_label": "",
|
||||
"settings_update_item_warning": "",
|
||||
"test_event_tooltip": ""
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
// ###################################
|
||||
|
||||
$defaultLang = "en_us";
|
||||
$allLanguages = [ "ar_ar", "ca_ca", "cs_cz", "de_de", "en_us", "es_es", "fa_fa", "fr_fr", "it_it", "nb_no", "pl_pl", "pt_br", "pt_pt", "ru_ru", "sv_sv", "tr_tr", "uk_ua", "zh_cn"];
|
||||
$allLanguages = [ "ar_ar", "ca_ca", "cs_cz", "de_de", "en_us", "es_es", "fa_fa", "fr_fr", "it_it", "ja_jp", "nb_no", "pl_pl", "pt_br", "pt_pt", "ru_ru", "sv_sv", "tr_tr", "uk_ua", "zh_cn"];
|
||||
|
||||
|
||||
global $db;
|
||||
@@ -23,6 +23,7 @@ switch($result){
|
||||
case 'Farsi (fa_fa)': $pia_lang_selected = 'fa_fa'; break;
|
||||
case 'French (fr_fr)': $pia_lang_selected = 'fr_fr'; break;
|
||||
case 'Italian (it_it)': $pia_lang_selected = 'it_it'; break;
|
||||
case 'Japanese (ja_jp)': $pia_lang_selected = 'ja_jp'; break;
|
||||
case 'Norwegian (nb_no)': $pia_lang_selected = 'nb_no'; break;
|
||||
case 'Polish (pl_pl)': $pia_lang_selected = 'pl_pl'; break;
|
||||
case 'Portuguese (pt_br)': $pia_lang_selected = 'pt_br'; break;
|
||||
|
||||
@@ -34,6 +34,6 @@ if __name__ == "__main__":
|
||||
current_path = os.path.dirname(os.path.abspath(__file__))
|
||||
# language codes can be found here: http://www.lingoes.net/en/translator/langcode.htm
|
||||
# "en_us.json" has to be first!
|
||||
json_files = [ "en_us.json", "ar_ar.json", "ca_ca.json", "cs_cz.json", "de_de.json", "es_es.json", "fa_fa.json", "fr_fr.json", "it_it.json", "nb_no.json", "pl_pl.json", "pt_br.json", "pt_pt.json", "ru_ru.json", "sv_sv.json", "tr_tr.json", "uk_ua.json", "zh_cn.json"]
|
||||
json_files = [ "en_us.json", "ar_ar.json", "ca_ca.json", "cs_cz.json", "de_de.json", "es_es.json", "fa_fa.json", "fr_fr.json", "it_it.json", "ja_jp.json", "nb_no.json", "pl_pl.json", "pt_br.json", "pt_pt.json", "ru_ru.json", "sv_sv.json", "tr_tr.json", "uk_ua.json", "zh_cn.json"]
|
||||
file_paths = [os.path.join(current_path, file) for file in json_files]
|
||||
merge_translations(file_paths[0], file_paths[1:])
|
||||
|
||||
@@ -761,4 +761,4 @@
|
||||
"settings_system_label": "Система",
|
||||
"settings_update_item_warning": "Обновить значение ниже. Будьте осторожны, следуя предыдущему формату. <b>Проверка не выполняется.</b>",
|
||||
"test_event_tooltip": "Сначала сохраните изменения, прежде чем проверять настройки."
|
||||
}
|
||||
}
|
||||
@@ -448,7 +448,7 @@
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "When scanning remote networks, NMAP can only retrieve the IP address, not the MAC address. Enabling this setting generates a fake MAC address from the IP address to track devices, but it may cause inconsistencies if IPs change or devices are rediscovered. Static IPs are recommended. Device type and icon will not be detected correctly. When unchecked, devices with empty MAC addresses are skipped."
|
||||
"string": "When scanning remote networks, NMAP can only retrieve the IP address, not the MAC address. Enabling the FAKE_MAC setting generates a fake MAC address from the IP address to track devices, but it may cause inconsistencies if IPs change or devices are re-discovered with a different MAC. Static IPs are recommended. Device type and icon might not be detected correctly and some plugins might fail if they depend on a valid MAC address. When unchecked, devices with empty MAC addresses are skipped."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -386,7 +386,7 @@ def importConfigs(pm, db, all_plugins):
|
||||
c_d,
|
||||
"Language Interface",
|
||||
'{"dataType":"string", "elements": [{"elementType" : "select", "elementOptions" : [] ,"transformers": []}]}',
|
||||
"['English (en_us)', 'Arabic (ar_ar)', 'Catalan (ca_ca)', 'Czech (cs_cz)', 'German (de_de)', 'Spanish (es_es)', 'Farsi (fa_fa)', 'French (fr_fr)', 'Italian (it_it)', 'Norwegian (nb_no)', 'Polish (pl_pl)', 'Portuguese (pt_br)', 'Portuguese (pt_pt)', 'Russian (ru_ru)', 'Swedish (sv_sv)', 'Turkish (tr_tr)', 'Ukrainian (uk_ua)', 'Chinese (zh_cn)']",
|
||||
"['English (en_us)', 'Arabic (ar_ar)', 'Catalan (ca_ca)', 'Czech (cs_cz)', 'German (de_de)', 'Spanish (es_es)', 'Farsi (fa_fa)', 'French (fr_fr)', 'Italian (it_it)', 'Japanese (ja_jp)', 'Norwegian (nb_no)', 'Polish (pl_pl)', 'Portuguese (pt_br)', 'Portuguese (pt_pt)', 'Russian (ru_ru)', 'Swedish (sv_sv)', 'Turkish (tr_tr)', 'Ukrainian (uk_ua)', 'Chinese (zh_cn)']",
|
||||
"UI",
|
||||
)
|
||||
|
||||
|
||||
@@ -1,324 +1,149 @@
|
||||
"""
|
||||
Unit tests for SafeConditionBuilder focusing on core security functionality.
|
||||
This test file has minimal dependencies to ensure it can run in any environment.
|
||||
Minimal pytest unit tests for SafeConditionBuilder security functionality.
|
||||
Focuses on core parsing, parameterization, and input sanitization.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
import re
|
||||
import pytest
|
||||
from unittest.mock import Mock
|
||||
import sys
|
||||
|
||||
# Mock the logger module to avoid dependency issues
|
||||
# Mock logger
|
||||
sys.modules['logger'] = Mock()
|
||||
|
||||
|
||||
# Standalone version of SafeConditionBuilder for testing
|
||||
class TestSafeConditionBuilder:
|
||||
"""
|
||||
Test version of SafeConditionBuilder with mock logger.
|
||||
"""
|
||||
class SafeConditionBuilderForTesting:
|
||||
"""Minimal SafeConditionBuilder implementation for tests."""
|
||||
|
||||
# Whitelist of allowed column names for filtering
|
||||
ALLOWED_COLUMNS = {
|
||||
'eve_MAC', 'eve_DateTime', 'eve_IP', 'eve_EventType', 'devName',
|
||||
'devComments', 'devLastIP', 'devVendor', 'devAlertEvents',
|
||||
'devAlertDown', 'devIsArchived', 'devPresentLastScan', 'devFavorite',
|
||||
'devIsNew', 'Plugin', 'Object_PrimaryId', 'Object_SecondaryId',
|
||||
'DateTimeChanged', 'Watched_Value1', 'Watched_Value2', 'Watched_Value3',
|
||||
'Watched_Value4', 'Status'
|
||||
}
|
||||
|
||||
# Whitelist of allowed comparison operators
|
||||
ALLOWED_OPERATORS = {
|
||||
'=', '!=', '<>', '<', '>', '<=', '>=', 'LIKE', 'NOT LIKE',
|
||||
'IN', 'NOT IN', 'IS NULL', 'IS NOT NULL'
|
||||
}
|
||||
|
||||
# Whitelist of allowed logical operators
|
||||
ALLOWED_COLUMNS = {'devName', 'eve_MAC', 'eve_EventType'}
|
||||
ALLOWED_OPERATORS = {'=', '!=', '<', '>', '<=', '>=', 'LIKE', 'NOT LIKE'}
|
||||
ALLOWED_LOGICAL_OPERATORS = {'AND', 'OR'}
|
||||
|
||||
# Whitelist of allowed event types
|
||||
ALLOWED_EVENT_TYPES = {
|
||||
'New Device', 'Connected', 'Disconnected', 'Device Down',
|
||||
'Down Reconnected', 'IP Changed'
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the SafeConditionBuilder."""
|
||||
self.parameters = {}
|
||||
self.param_counter = 0
|
||||
|
||||
def _generate_param_name(self, prefix='param'):
|
||||
"""Generate a unique parameter name for SQL binding."""
|
||||
def _generate_param_name(self):
|
||||
self.param_counter += 1
|
||||
return f"{prefix}_{self.param_counter}"
|
||||
return f"param_{self.param_counter}"
|
||||
|
||||
def _sanitize_string(self, value):
|
||||
"""Sanitize string input by removing potentially dangerous characters."""
|
||||
if not isinstance(value, str):
|
||||
return str(value)
|
||||
|
||||
# Replace {s-quote} placeholder with single quote (maintaining compatibility)
|
||||
value = str(value)
|
||||
value = value.replace('{s-quote}', "'")
|
||||
|
||||
# Remove any null bytes, control characters, and excessive whitespace
|
||||
value = re.sub(r'[\x00-\x08\x0b\x0c\x0e-\x1f\x7f-\x84\x86-\x9f]', '', value)
|
||||
value = re.sub(r'\s+', ' ', value.strip())
|
||||
|
||||
return value
|
||||
return re.sub(r'\s+', ' ', value.strip())
|
||||
|
||||
def _validate_column_name(self, column):
|
||||
"""Validate that a column name is in the whitelist."""
|
||||
return column in self.ALLOWED_COLUMNS
|
||||
|
||||
def _validate_operator(self, operator):
|
||||
"""Validate that an operator is in the whitelist."""
|
||||
return operator.upper() in self.ALLOWED_OPERATORS
|
||||
|
||||
def _validate_logical_operator(self, logical_op):
|
||||
"""Validate that a logical operator is in the whitelist."""
|
||||
return logical_op.upper() in self.ALLOWED_LOGICAL_OPERATORS
|
||||
|
||||
def build_safe_condition(self, condition_string):
|
||||
"""Parse and build a safe SQL condition from a user-provided string."""
|
||||
if not condition_string or not condition_string.strip():
|
||||
return "", {}
|
||||
|
||||
# Sanitize the input
|
||||
condition_string = self._sanitize_string(condition_string)
|
||||
|
||||
# Reset parameters for this condition
|
||||
self.parameters = {}
|
||||
self.param_counter = 0
|
||||
|
||||
try:
|
||||
return self._parse_condition(condition_string)
|
||||
except Exception:
|
||||
raise ValueError(f"Invalid condition format: {condition_string}")
|
||||
|
||||
def _parse_condition(self, condition):
|
||||
"""Parse a condition string into safe SQL with parameters."""
|
||||
condition = condition.strip()
|
||||
|
||||
# Handle empty conditions
|
||||
if not condition:
|
||||
return "", {}
|
||||
|
||||
# Simple pattern matching for common conditions
|
||||
# Pattern 1: AND/OR column operator value
|
||||
pattern1 = r"^\s*(AND|OR)?\s+(\w+)\s+(=|!=|<>|<|>|<=|>=|LIKE|NOT\s+LIKE)\s+'(.+?)'\s*$"
|
||||
|
||||
match1 = re.match(pattern1, condition, re.IGNORECASE)
|
||||
|
||||
if match1:
|
||||
logical_op, column, operator, value = match1.groups()
|
||||
return self._build_simple_condition(logical_op, column, operator, value)
|
||||
|
||||
# If no patterns match, reject the condition for security
|
||||
raise ValueError(f"Unsupported condition pattern: {condition}")
|
||||
|
||||
def _build_simple_condition(self, logical_op, column, operator, value):
|
||||
"""Build a simple condition with parameter binding."""
|
||||
# Validate components
|
||||
pattern = r"^\s*(AND|OR)?\s+(\w+)\s+(=|!=|<>|<|>|<=|>=|LIKE|NOT\s+LIKE)\s+'(.+?)'\s*$"
|
||||
match = re.match(pattern, condition_string, re.IGNORECASE)
|
||||
if not match:
|
||||
raise ValueError("Unsupported condition pattern")
|
||||
logical_op, column, operator, value = match.groups()
|
||||
if not self._validate_column_name(column):
|
||||
raise ValueError(f"Invalid column name: {column}")
|
||||
|
||||
raise ValueError(f"Invalid column: {column}")
|
||||
if not self._validate_operator(operator):
|
||||
raise ValueError(f"Invalid operator: {operator}")
|
||||
|
||||
if logical_op and not self._validate_logical_operator(logical_op):
|
||||
raise ValueError(f"Invalid logical operator: {logical_op}")
|
||||
|
||||
# Generate parameter name and store value
|
||||
param_name = self._generate_param_name()
|
||||
self.parameters[param_name] = value
|
||||
|
||||
# Build the SQL snippet
|
||||
sql_parts = []
|
||||
if logical_op:
|
||||
sql_parts.append(logical_op.upper())
|
||||
|
||||
sql_parts.extend([column, operator.upper(), f":{param_name}"])
|
||||
|
||||
return " ".join(sql_parts), self.parameters
|
||||
|
||||
def get_safe_condition_legacy(self, condition_setting):
|
||||
"""Convert legacy condition settings to safe parameterized queries."""
|
||||
if not condition_setting or not condition_setting.strip():
|
||||
return "", {}
|
||||
|
||||
try:
|
||||
return self.build_safe_condition(condition_setting)
|
||||
except ValueError:
|
||||
# Log the error and return empty condition for safety
|
||||
return "", {}
|
||||
# -----------------------
|
||||
# Pytest Fixtures
|
||||
# -----------------------
|
||||
@pytest.fixture
|
||||
def builder():
|
||||
return SafeConditionBuilderForTesting()
|
||||
|
||||
|
||||
class TestSafeConditionBuilderSecurity(unittest.TestCase):
|
||||
"""Test cases for the SafeConditionBuilder security functionality."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures before each test method."""
|
||||
self.builder = TestSafeConditionBuilder()
|
||||
|
||||
def test_initialization(self):
|
||||
"""Test that SafeConditionBuilder initializes correctly."""
|
||||
self.assertIsInstance(self.builder, TestSafeConditionBuilder)
|
||||
self.assertEqual(self.builder.param_counter, 0)
|
||||
self.assertEqual(self.builder.parameters, {})
|
||||
|
||||
def test_sanitize_string(self):
|
||||
"""Test string sanitization functionality."""
|
||||
# Test normal string
|
||||
result = self.builder._sanitize_string("normal string")
|
||||
self.assertEqual(result, "normal string")
|
||||
|
||||
# Test s-quote replacement
|
||||
result = self.builder._sanitize_string("test{s-quote}value")
|
||||
self.assertEqual(result, "test'value")
|
||||
|
||||
# Test control character removal
|
||||
result = self.builder._sanitize_string("test\x00\x01string")
|
||||
self.assertEqual(result, "teststring")
|
||||
|
||||
# Test excessive whitespace
|
||||
result = self.builder._sanitize_string(" test string ")
|
||||
self.assertEqual(result, "test string")
|
||||
|
||||
def test_validate_column_name(self):
|
||||
"""Test column name validation against whitelist."""
|
||||
# Valid columns
|
||||
self.assertTrue(self.builder._validate_column_name('eve_MAC'))
|
||||
self.assertTrue(self.builder._validate_column_name('devName'))
|
||||
self.assertTrue(self.builder._validate_column_name('eve_EventType'))
|
||||
|
||||
# Invalid columns
|
||||
self.assertFalse(self.builder._validate_column_name('malicious_column'))
|
||||
self.assertFalse(self.builder._validate_column_name('drop_table'))
|
||||
self.assertFalse(self.builder._validate_column_name('user_input'))
|
||||
|
||||
def test_validate_operator(self):
|
||||
"""Test operator validation against whitelist."""
|
||||
# Valid operators
|
||||
self.assertTrue(self.builder._validate_operator('='))
|
||||
self.assertTrue(self.builder._validate_operator('LIKE'))
|
||||
self.assertTrue(self.builder._validate_operator('IN'))
|
||||
|
||||
# Invalid operators
|
||||
self.assertFalse(self.builder._validate_operator('UNION'))
|
||||
self.assertFalse(self.builder._validate_operator('DROP'))
|
||||
self.assertFalse(self.builder._validate_operator('EXEC'))
|
||||
|
||||
def test_build_simple_condition_valid(self):
|
||||
"""Test building valid simple conditions."""
|
||||
sql, params = self.builder._build_simple_condition('AND', 'devName', '=', 'TestDevice')
|
||||
|
||||
self.assertIn('AND devName = :param_', sql)
|
||||
self.assertEqual(len(params), 1)
|
||||
self.assertIn('TestDevice', params.values())
|
||||
|
||||
def test_build_simple_condition_invalid_column(self):
|
||||
"""Test that invalid column names are rejected."""
|
||||
with self.assertRaises(ValueError) as context:
|
||||
self.builder._build_simple_condition('AND', 'invalid_column', '=', 'value')
|
||||
|
||||
self.assertIn('Invalid column name', str(context.exception))
|
||||
|
||||
def test_build_simple_condition_invalid_operator(self):
|
||||
"""Test that invalid operators are rejected."""
|
||||
with self.assertRaises(ValueError) as context:
|
||||
self.builder._build_simple_condition('AND', 'devName', 'UNION', 'value')
|
||||
|
||||
self.assertIn('Invalid operator', str(context.exception))
|
||||
|
||||
def test_legacy_condition_compatibility(self):
|
||||
"""Test backward compatibility with legacy condition formats."""
|
||||
# Test simple condition
|
||||
sql, params = self.builder.get_safe_condition_legacy("AND devName = 'TestDevice'")
|
||||
self.assertIn('devName', sql)
|
||||
self.assertIn('TestDevice', params.values())
|
||||
|
||||
# Test empty condition
|
||||
sql, params = self.builder.get_safe_condition_legacy("")
|
||||
self.assertEqual(sql, "")
|
||||
self.assertEqual(params, {})
|
||||
|
||||
# Test invalid condition returns empty
|
||||
sql, params = self.builder.get_safe_condition_legacy("INVALID SQL INJECTION")
|
||||
self.assertEqual(sql, "")
|
||||
self.assertEqual(params, {})
|
||||
|
||||
def test_parameter_generation(self):
|
||||
"""Test that parameters are generated correctly and do not leak between calls."""
|
||||
# First condition
|
||||
sql1, params1 = self.builder.build_safe_condition("AND devName = 'Device1'")
|
||||
self.assertEqual(len(params1), 1)
|
||||
self.assertIn("Device1", params1.values())
|
||||
|
||||
# Second condition
|
||||
sql2, params2 = self.builder.build_safe_condition("AND devName = 'Device2'")
|
||||
self.assertEqual(len(params2), 1)
|
||||
self.assertIn("Device2", params2.values())
|
||||
|
||||
# Ensure no leakage between calls
|
||||
self.assertNotEqual(params1, params2)
|
||||
|
||||
def test_xss_prevention(self):
|
||||
"""Test that XSS-like payloads in device names are handled safely."""
|
||||
xss_payloads = [
|
||||
"<script>alert('xss')</script>",
|
||||
"javascript:alert(1)",
|
||||
"<img src=x onerror=alert(1)>",
|
||||
"'; DROP TABLE users; SELECT '<script>alert(1)</script>' --"
|
||||
]
|
||||
|
||||
for payload in xss_payloads:
|
||||
with self.subTest(payload=payload):
|
||||
# Should either process safely or reject
|
||||
try:
|
||||
sql, params = self.builder.build_safe_condition(f"AND devName = '{payload}'")
|
||||
# If processed, should be parameterized
|
||||
self.assertIn(':', sql)
|
||||
self.assertIn(payload, params.values())
|
||||
except ValueError:
|
||||
# Rejection is also acceptable for safety
|
||||
pass
|
||||
|
||||
def test_unicode_handling(self):
|
||||
"""Test that Unicode characters are handled properly."""
|
||||
unicode_strings = [
|
||||
"Ülrich's Device",
|
||||
"Café Network",
|
||||
"测试设备",
|
||||
"Устройство"
|
||||
]
|
||||
|
||||
for unicode_str in unicode_strings:
|
||||
with self.subTest(unicode_str=unicode_str):
|
||||
sql, params = self.builder.build_safe_condition(f"AND devName = '{unicode_str}'")
|
||||
self.assertIn(unicode_str, params.values())
|
||||
|
||||
def test_edge_cases(self):
|
||||
"""Test edge cases and boundary conditions."""
|
||||
edge_cases = [
|
||||
"", # Empty string
|
||||
" ", # Whitespace only
|
||||
"AND devName = ''", # Empty value
|
||||
"AND devName = 'a'", # Single character
|
||||
"AND devName = '" + "x" * 1000 + "'", # Very long string
|
||||
]
|
||||
|
||||
for case in edge_cases:
|
||||
with self.subTest(case=case):
|
||||
try:
|
||||
sql, params = self.builder.get_safe_condition_legacy(case)
|
||||
# Should either return valid result or empty safe result
|
||||
self.assertIsInstance(sql, str)
|
||||
self.assertIsInstance(params, dict)
|
||||
except Exception:
|
||||
self.fail(f"Unexpected exception for edge case: {case}")
|
||||
# -----------------------
|
||||
# Tests
|
||||
# -----------------------
|
||||
def test_sanitize_string(builder):
|
||||
assert builder._sanitize_string(" test string ") == "test string"
|
||||
assert builder._sanitize_string("test{s-quote}value") == "test'value"
|
||||
assert builder._sanitize_string("test\x00\x01string") == "teststring"
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Run the test suite
|
||||
unittest.main(verbosity=2)
|
||||
def test_validate_column_and_operator(builder):
|
||||
assert builder._validate_column_name('devName')
|
||||
assert not builder._validate_column_name('bad_column')
|
||||
assert builder._validate_operator('=')
|
||||
assert not builder._validate_operator('DROP')
|
||||
|
||||
|
||||
def test_build_simple_condition_valid(builder):
|
||||
sql, params = builder.build_safe_condition("AND devName = 'Device1'")
|
||||
assert 'AND devName = :param_' in sql
|
||||
assert "Device1" in params.values()
|
||||
|
||||
|
||||
def test_build_simple_condition_invalid(builder):
|
||||
with pytest.raises(ValueError):
|
||||
builder.build_safe_condition("AND bad_column = 'X'")
|
||||
with pytest.raises(ValueError):
|
||||
builder.build_safe_condition("AND devName UNION 'X'")
|
||||
|
||||
|
||||
def test_parameter_isolation(builder):
|
||||
sql1, params1 = builder.build_safe_condition("AND devName = 'Device1'")
|
||||
sql2, params2 = builder.build_safe_condition("AND devName = 'Device2'")
|
||||
assert params1 != params2
|
||||
assert "Device1" in params1.values()
|
||||
assert "Device2" in params2.values()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("payload", [
|
||||
"<script>alert('xss')</script>",
|
||||
"javascript:alert(1)",
|
||||
"'; DROP TABLE users; --"
|
||||
])
|
||||
def test_xss_payloads(builder, payload):
|
||||
sql, params = builder.build_safe_condition(f"AND devName = '{payload}'")
|
||||
assert ':' in sql
|
||||
assert payload in params.values()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("unicode_str", [
|
||||
"Ülrich's Device",
|
||||
"Café Network",
|
||||
"测试设备",
|
||||
"Устройство"
|
||||
])
|
||||
def test_unicode_support(builder, unicode_str):
|
||||
sql, params = builder.build_safe_condition(f"AND devName = '{unicode_str}'")
|
||||
assert unicode_str in params.values()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("case", [
|
||||
"", " ", "AND devName = ''", "AND devName = 'a'", "AND devName = '" + "x"*500 + "'"
|
||||
])
|
||||
def test_edge_cases(builder, case):
|
||||
try:
|
||||
sql, params = builder.build_safe_condition(case) if case.strip() else ("", {})
|
||||
assert isinstance(sql, str)
|
||||
assert isinstance(params, dict)
|
||||
except ValueError:
|
||||
# Empty or invalid inputs can raise ValueError, acceptable
|
||||
pass
|
||||
|
||||
@@ -1,448 +1,237 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
NetAlertX SQL Injection Fix - Integration Testing
|
||||
Validates the complete implementation as requested by maintainer jokob-sk
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import sqlite3
|
||||
import json
|
||||
import unittest
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
import tempfile
|
||||
import subprocess
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
# Add server paths
|
||||
import sys
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'server'))
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'server', 'db'))
|
||||
|
||||
# Import our modules
|
||||
from db.sql_safe_builder import SafeConditionBuilder, create_safe_condition_builder
|
||||
from messaging.reporting import get_notifications
|
||||
|
||||
class NetAlertXIntegrationTest(unittest.TestCase):
|
||||
"""
|
||||
Comprehensive integration tests to validate:
|
||||
1. Fresh install compatibility
|
||||
2. Existing DB/config compatibility
|
||||
3. Notification system integration
|
||||
4. Settings persistence
|
||||
5. Device operations
|
||||
6. Plugin functionality
|
||||
7. Error handling
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test environment"""
|
||||
self.test_db_path = tempfile.mktemp(suffix='.db')
|
||||
self.builder = SafeConditionBuilder()
|
||||
self.create_test_database()
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test environment"""
|
||||
if os.path.exists(self.test_db_path):
|
||||
os.remove(self.test_db_path)
|
||||
|
||||
def create_test_database(self):
|
||||
"""Create test database with NetAlertX schema"""
|
||||
conn = sqlite3.connect(self.test_db_path)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# Create minimal schema for testing
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS Events_Devices (
|
||||
eve_MAC TEXT,
|
||||
eve_DateTime TEXT,
|
||||
devLastIP TEXT,
|
||||
eve_EventType TEXT,
|
||||
devName TEXT,
|
||||
devComments TEXT,
|
||||
eve_PendingAlertEmail INTEGER
|
||||
)
|
||||
''')
|
||||
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS Devices (
|
||||
devMac TEXT PRIMARY KEY,
|
||||
devName TEXT,
|
||||
devComments TEXT,
|
||||
devAlertEvents INTEGER DEFAULT 1,
|
||||
devAlertDown INTEGER DEFAULT 1
|
||||
)
|
||||
''')
|
||||
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS Events (
|
||||
eve_MAC TEXT,
|
||||
eve_DateTime TEXT,
|
||||
eve_EventType TEXT,
|
||||
eve_PendingAlertEmail INTEGER
|
||||
)
|
||||
''')
|
||||
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS Plugins_Events (
|
||||
Plugin TEXT,
|
||||
Object_PrimaryId TEXT,
|
||||
Object_SecondaryId TEXT,
|
||||
DateTimeChanged TEXT,
|
||||
Watched_Value1 TEXT,
|
||||
Watched_Value2 TEXT,
|
||||
Watched_Value3 TEXT,
|
||||
Watched_Value4 TEXT,
|
||||
Status TEXT
|
||||
)
|
||||
''')
|
||||
|
||||
# Insert test data
|
||||
test_data = [
|
||||
('aa:bb:cc:dd:ee:ff', '2024-01-01 12:00:00', '192.168.1.100', 'New Device', 'Test Device', 'Test Comment', 1),
|
||||
('11:22:33:44:55:66', '2024-01-01 12:01:00', '192.168.1.101', 'Connected', 'Test Device 2', 'Another Comment', 1),
|
||||
('77:88:99:aa:bb:cc', '2024-01-01 12:02:00', '192.168.1.102', 'Disconnected', 'Test Device 3', 'Third Comment', 1),
|
||||
]
|
||||
|
||||
cursor.executemany('''
|
||||
INSERT INTO Events_Devices (eve_MAC, eve_DateTime, devLastIP, eve_EventType, devName, devComments, eve_PendingAlertEmail)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
''', test_data)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
def test_1_fresh_install_compatibility(self):
|
||||
"""Test 1: Fresh install (no DB/config)"""
|
||||
print("\n=== TEST 1: Fresh Install Compatibility ===")
|
||||
|
||||
# Test SafeConditionBuilder initialization
|
||||
builder = create_safe_condition_builder()
|
||||
self.assertIsInstance(builder, SafeConditionBuilder)
|
||||
|
||||
# Test empty condition handling
|
||||
condition, params = builder.get_safe_condition_legacy("")
|
||||
self.assertEqual(condition, "")
|
||||
self.assertEqual(params, {})
|
||||
|
||||
# Test basic valid condition
|
||||
condition, params = builder.get_safe_condition_legacy("AND devName = 'TestDevice'")
|
||||
self.assertIn("devName = :", condition)
|
||||
self.assertIn('TestDevice', list(params.values()))
|
||||
|
||||
print("✅ Fresh install compatibility: PASSED")
|
||||
|
||||
def test_2_existing_db_compatibility(self):
|
||||
"""Test 2: Existing DB/config compatibility"""
|
||||
print("\n=== TEST 2: Existing DB/Config Compatibility ===")
|
||||
|
||||
# Mock database connection
|
||||
mock_db = Mock()
|
||||
mock_sql = Mock()
|
||||
mock_db.sql = mock_sql
|
||||
mock_db.get_table_as_json = Mock()
|
||||
|
||||
# Mock return value for get_table_as_json
|
||||
mock_result = Mock()
|
||||
mock_result.columnNames = ['MAC', 'Datetime', 'IP', 'Event Type', 'Device name', 'Comments']
|
||||
mock_result.json = {'data': []}
|
||||
mock_db.get_table_as_json.return_value = mock_result
|
||||
|
||||
# Mock settings
|
||||
with patch('messaging.reporting.get_setting_value') as mock_settings:
|
||||
mock_settings.side_effect = lambda key: {
|
||||
'NTFPRCS_INCLUDED_SECTIONS': ['new_devices', 'events'],
|
||||
'NTFPRCS_new_dev_condition': "AND devName = 'TestDevice'",
|
||||
'NTFPRCS_event_condition': "AND devComments LIKE '%test%'",
|
||||
'NTFPRCS_alert_down_time': '60'
|
||||
}.get(key, '')
|
||||
|
||||
with patch('messaging.reporting.get_timezone_offset', return_value='+00:00'):
|
||||
# Test get_notifications function
|
||||
result = get_notifications(mock_db)
|
||||
|
||||
# Verify structure
|
||||
self.assertIn('new_devices', result)
|
||||
self.assertIn('events', result)
|
||||
self.assertIn('new_devices_meta', result)
|
||||
self.assertIn('events_meta', result)
|
||||
|
||||
# Verify parameterized queries were called
|
||||
self.assertTrue(mock_db.get_table_as_json.called)
|
||||
|
||||
# Check that calls used parameters (not direct concatenation)
|
||||
calls = mock_db.get_table_as_json.call_args_list
|
||||
for call in calls:
|
||||
args, kwargs = call
|
||||
if len(args) > 1: # Has parameters
|
||||
self.assertIsInstance(args[1], dict) # Parameters should be dict
|
||||
|
||||
print("✅ Existing DB/config compatibility: PASSED")
|
||||
|
||||
def test_3_notification_system_integration(self):
|
||||
"""Test 3: Notification testing integration"""
|
||||
print("\n=== TEST 3: Notification System Integration ===")
|
||||
|
||||
# Test that SafeConditionBuilder integrates with notification queries
|
||||
builder = create_safe_condition_builder()
|
||||
|
||||
# Test email notification conditions
|
||||
email_condition = "AND devName = 'EmailTestDevice'"
|
||||
condition, params = builder.get_safe_condition_legacy(email_condition)
|
||||
self.assertIn("devName = :", condition)
|
||||
self.assertIn('EmailTestDevice', list(params.values()))
|
||||
|
||||
# Test Apprise notification conditions
|
||||
apprise_condition = "AND eve_EventType = 'Connected'"
|
||||
condition, params = builder.get_safe_condition_legacy(apprise_condition)
|
||||
self.assertIn("eve_EventType = :", condition)
|
||||
self.assertIn('Connected', list(params.values()))
|
||||
|
||||
# Test webhook notification conditions
|
||||
webhook_condition = "AND devComments LIKE '%webhook%'"
|
||||
condition, params = builder.get_safe_condition_legacy(webhook_condition)
|
||||
self.assertIn("devComments LIKE :", condition)
|
||||
self.assertIn('%webhook%', list(params.values()))
|
||||
|
||||
# Test MQTT notification conditions
|
||||
mqtt_condition = "AND eve_MAC = 'aa:bb:cc:dd:ee:ff'"
|
||||
condition, params = builder.get_safe_condition_legacy(mqtt_condition)
|
||||
self.assertIn("eve_MAC = :", condition)
|
||||
self.assertIn('aa:bb:cc:dd:ee:ff', list(params.values()))
|
||||
|
||||
print("✅ Notification system integration: PASSED")
|
||||
|
||||
def test_4_settings_persistence(self):
|
||||
"""Test 4: Settings persistence"""
|
||||
print("\n=== TEST 4: Settings Persistence ===")
|
||||
|
||||
# Test various setting formats that should be supported
|
||||
test_settings = [
|
||||
"AND devName = 'Persistent Device'",
|
||||
"AND devComments = {s-quote}Legacy Quote{s-quote}",
|
||||
"AND eve_EventType IN ('Connected', 'Disconnected')",
|
||||
"AND devLastIP = '192.168.1.1'",
|
||||
"" # Empty setting should work
|
||||
]
|
||||
|
||||
builder = create_safe_condition_builder()
|
||||
|
||||
for setting in test_settings:
|
||||
try:
|
||||
condition, params = builder.get_safe_condition_legacy(setting)
|
||||
# Should not raise exception
|
||||
self.assertIsInstance(condition, str)
|
||||
self.assertIsInstance(params, dict)
|
||||
except Exception as e:
|
||||
if setting != "": # Empty is allowed to "fail" gracefully
|
||||
self.fail(f"Setting '{setting}' failed: {e}")
|
||||
|
||||
print("✅ Settings persistence: PASSED")
|
||||
|
||||
def test_5_device_operations(self):
|
||||
"""Test 5: Device operations"""
|
||||
print("\n=== TEST 5: Device Operations ===")
|
||||
|
||||
# Test device-related conditions
|
||||
builder = create_safe_condition_builder()
|
||||
|
||||
device_conditions = [
|
||||
"AND devName = 'Updated Device'",
|
||||
"AND devMac = 'aa:bb:cc:dd:ee:ff'",
|
||||
"AND devComments = 'Device updated successfully'",
|
||||
"AND devLastIP = '192.168.1.200'"
|
||||
]
|
||||
|
||||
for condition in device_conditions:
|
||||
safe_condition, params = builder.get_safe_condition_legacy(condition)
|
||||
self.assertTrue(len(params) > 0 or safe_condition == "")
|
||||
# Ensure no direct string concatenation in output
|
||||
self.assertNotIn("'", safe_condition) # No literal quotes in SQL
|
||||
|
||||
print("✅ Device operations: PASSED")
|
||||
|
||||
def test_6_plugin_functionality(self):
|
||||
"""Test 6: Plugin functionality"""
|
||||
print("\n=== TEST 6: Plugin Functionality ===")
|
||||
|
||||
# Test plugin-related conditions that might be used
|
||||
builder = create_safe_condition_builder()
|
||||
|
||||
plugin_conditions = [
|
||||
"AND Plugin = 'TestPlugin'",
|
||||
"AND Object_PrimaryId = 'primary123'",
|
||||
"AND Status = 'Active'"
|
||||
]
|
||||
|
||||
for condition in plugin_conditions:
|
||||
safe_condition, params = builder.get_safe_condition_legacy(condition)
|
||||
if safe_condition: # If condition was accepted
|
||||
self.assertIn(":", safe_condition) # Should have parameter placeholder
|
||||
self.assertTrue(len(params) > 0) # Should have parameters
|
||||
|
||||
# Test that plugin data structure is preserved
|
||||
mock_db = Mock()
|
||||
mock_db.sql = Mock()
|
||||
mock_result = Mock()
|
||||
mock_result.columnNames = ['Plugin', 'Object_PrimaryId', 'Status']
|
||||
mock_result.json = {'data': []}
|
||||
mock_db.get_table_as_json.return_value = mock_result
|
||||
|
||||
with patch('messaging.reporting.get_setting_value') as mock_settings:
|
||||
mock_settings.side_effect = lambda key: {
|
||||
'NTFPRCS_INCLUDED_SECTIONS': ['plugins']
|
||||
}.get(key, '')
|
||||
|
||||
# -----------------------------
|
||||
# Fixtures
|
||||
# -----------------------------
|
||||
@pytest.fixture
|
||||
def test_db_path():
|
||||
path = tempfile.mktemp(suffix=".db")
|
||||
yield path
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
|
||||
@pytest.fixture
|
||||
def builder():
|
||||
return create_safe_condition_builder()
|
||||
|
||||
@pytest.fixture
|
||||
def test_db(test_db_path):
|
||||
conn = sqlite3.connect(test_db_path)
|
||||
cur = conn.cursor()
|
||||
|
||||
# Minimal schema for integration testing
|
||||
cur.execute('''
|
||||
CREATE TABLE IF NOT EXISTS Events_Devices (
|
||||
eve_MAC TEXT,
|
||||
eve_DateTime TEXT,
|
||||
devLastIP TEXT,
|
||||
eve_EventType TEXT,
|
||||
devName TEXT,
|
||||
devComments TEXT,
|
||||
eve_PendingAlertEmail INTEGER
|
||||
)
|
||||
''')
|
||||
|
||||
cur.execute('''
|
||||
CREATE TABLE IF NOT EXISTS Devices (
|
||||
devMac TEXT PRIMARY KEY,
|
||||
devName TEXT,
|
||||
devComments TEXT,
|
||||
devAlertEvents INTEGER DEFAULT 1,
|
||||
devAlertDown INTEGER DEFAULT 1
|
||||
)
|
||||
''')
|
||||
|
||||
cur.execute('''
|
||||
CREATE TABLE IF NOT EXISTS Events (
|
||||
eve_MAC TEXT,
|
||||
eve_DateTime TEXT,
|
||||
eve_EventType TEXT,
|
||||
eve_PendingAlertEmail INTEGER
|
||||
)
|
||||
''')
|
||||
|
||||
cur.execute('''
|
||||
CREATE TABLE IF NOT EXISTS Plugins_Events (
|
||||
Plugin TEXT,
|
||||
Object_PrimaryId TEXT,
|
||||
Object_SecondaryId TEXT,
|
||||
DateTimeChanged TEXT,
|
||||
Watched_Value1 TEXT,
|
||||
Watched_Value2 TEXT,
|
||||
Watched_Value3 TEXT,
|
||||
Watched_Value4 TEXT,
|
||||
Status TEXT
|
||||
)
|
||||
''')
|
||||
|
||||
# Insert test data
|
||||
test_data = [
|
||||
('aa:bb:cc:dd:ee:ff', '2024-01-01 12:00:00', '192.168.1.100', 'New Device', 'Test Device', 'Test Comment', 1),
|
||||
('11:22:33:44:55:66', '2024-01-01 12:01:00', '192.168.1.101', 'Connected', 'Test Device 2', 'Another Comment', 1),
|
||||
('77:88:99:aa:bb:cc', '2024-01-01 12:02:00', '192.168.1.102', 'Disconnected', 'Test Device 3', 'Third Comment', 1),
|
||||
]
|
||||
cur.executemany('''
|
||||
INSERT INTO Events_Devices (eve_MAC, eve_DateTime, devLastIP, eve_EventType, devName, devComments, eve_PendingAlertEmail)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
''', test_data)
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return test_db_path
|
||||
|
||||
# -----------------------------
|
||||
# Tests
|
||||
# -----------------------------
|
||||
|
||||
def test_fresh_install_compatibility(builder):
|
||||
condition, params = builder.get_safe_condition_legacy("")
|
||||
assert condition == ""
|
||||
assert params == {}
|
||||
|
||||
condition, params = builder.get_safe_condition_legacy("AND devName = 'TestDevice'")
|
||||
assert "devName = :" in condition
|
||||
assert 'TestDevice' in params.values()
|
||||
|
||||
def test_existing_db_compatibility():
|
||||
mock_db = Mock()
|
||||
mock_result = Mock()
|
||||
mock_result.columnNames = ['MAC', 'Datetime', 'IP', 'Event Type', 'Device name', 'Comments']
|
||||
mock_result.json = {'data': []}
|
||||
mock_db.get_table_as_json.return_value = mock_result
|
||||
|
||||
with patch('messaging.reporting.get_setting_value') as s:
|
||||
s.side_effect = lambda k: {
|
||||
'NTFPRCS_INCLUDED_SECTIONS': ['new_devices', 'events'],
|
||||
'NTFPRCS_new_dev_condition': "AND devName = 'TestDevice'",
|
||||
'NTFPRCS_event_condition': "AND devComments LIKE '%test%'",
|
||||
'NTFPRCS_alert_down_time': '60'
|
||||
}.get(k, '')
|
||||
|
||||
with patch('messaging.reporting.get_timezone_offset', return_value='+00:00'):
|
||||
result = get_notifications(mock_db)
|
||||
self.assertIn('plugins', result)
|
||||
self.assertIn('plugins_meta', result)
|
||||
|
||||
print("✅ Plugin functionality: PASSED")
|
||||
|
||||
def test_7_sql_injection_prevention(self):
|
||||
"""Test 7: SQL injection prevention (critical security test)"""
|
||||
print("\n=== TEST 7: SQL Injection Prevention ===")
|
||||
|
||||
# Test malicious inputs are properly blocked
|
||||
malicious_inputs = [
|
||||
"'; DROP TABLE Events_Devices; --",
|
||||
"' OR '1'='1",
|
||||
"1' UNION SELECT * FROM Devices --",
|
||||
"'; INSERT INTO Events VALUES ('hacked'); --",
|
||||
"' AND (SELECT COUNT(*) FROM sqlite_master) > 0 --"
|
||||
]
|
||||
|
||||
builder = create_safe_condition_builder()
|
||||
|
||||
for malicious_input in malicious_inputs:
|
||||
condition, params = builder.get_safe_condition_legacy(malicious_input)
|
||||
# All malicious inputs should result in empty/safe condition
|
||||
self.assertEqual(condition, "", f"Malicious input not blocked: {malicious_input}")
|
||||
self.assertEqual(params, {}, f"Parameters returned for malicious input: {malicious_input}")
|
||||
|
||||
print("✅ SQL injection prevention: PASSED")
|
||||
|
||||
def test_8_error_log_inspection(self):
|
||||
"""Test 8: Error handling and logging"""
|
||||
print("\n=== TEST 8: Error Handling and Logging ===")
|
||||
|
||||
# Test that invalid inputs are logged properly
|
||||
builder = create_safe_condition_builder()
|
||||
|
||||
# This should log an error but not crash
|
||||
invalid_condition = "INVALID SQL SYNTAX HERE"
|
||||
condition, params = builder.get_safe_condition_legacy(invalid_condition)
|
||||
|
||||
# Should return empty/safe values
|
||||
self.assertEqual(condition, "")
|
||||
self.assertEqual(params, {})
|
||||
|
||||
# Test edge cases
|
||||
edge_cases = [
|
||||
None, # This would cause TypeError in unpatched version
|
||||
"",
|
||||
" ",
|
||||
"\n\t",
|
||||
"AND column_not_in_whitelist = 'value'"
|
||||
]
|
||||
|
||||
for case in edge_cases:
|
||||
try:
|
||||
if case is not None:
|
||||
condition, params = builder.get_safe_condition_legacy(case)
|
||||
self.assertIsInstance(condition, str)
|
||||
self.assertIsInstance(params, dict)
|
||||
except Exception as e:
|
||||
# Should not crash on any input
|
||||
self.fail(f"Unexpected exception for input {case}: {e}")
|
||||
|
||||
print("✅ Error handling and logging: PASSED")
|
||||
|
||||
def test_9_backward_compatibility(self):
|
||||
"""Test 9: Backward compatibility with legacy settings"""
|
||||
print("\n=== TEST 9: Backward Compatibility ===")
|
||||
|
||||
# Test legacy {s-quote} placeholder support
|
||||
builder = create_safe_condition_builder()
|
||||
|
||||
legacy_conditions = [
|
||||
"AND devName = {s-quote}Legacy Device{s-quote}",
|
||||
"AND devComments = {s-quote}Old Style Quote{s-quote}",
|
||||
"AND devName = 'Normal Quote'" # Modern style should still work
|
||||
]
|
||||
|
||||
for legacy_condition in legacy_conditions:
|
||||
condition, params = builder.get_safe_condition_legacy(legacy_condition)
|
||||
if condition: # If accepted as valid
|
||||
# Should not contain the {s-quote} placeholder in output
|
||||
self.assertNotIn("{s-quote}", condition)
|
||||
# Should have proper parameter binding
|
||||
self.assertIn(":", condition)
|
||||
self.assertTrue(len(params) > 0)
|
||||
|
||||
print("✅ Backward compatibility: PASSED")
|
||||
|
||||
def test_10_performance_impact(self):
|
||||
"""Test 10: Performance impact measurement"""
|
||||
print("\n=== TEST 10: Performance Impact ===")
|
||||
|
||||
import time
|
||||
|
||||
builder = create_safe_condition_builder()
|
||||
|
||||
# Test performance of condition building
|
||||
test_condition = "AND devName = 'Performance Test Device'"
|
||||
|
||||
start_time = time.time()
|
||||
for _ in range(1000): # Run 1000 times
|
||||
condition, params = builder.get_safe_condition_legacy(test_condition)
|
||||
end_time = time.time()
|
||||
|
||||
total_time = end_time - start_time
|
||||
avg_time_ms = (total_time / 1000) * 1000
|
||||
|
||||
print(f"Average condition building time: {avg_time_ms:.3f}ms")
|
||||
|
||||
# Should be under 1ms per condition
|
||||
self.assertLess(avg_time_ms, 1.0, "Performance regression detected")
|
||||
|
||||
print("✅ Performance impact: PASSED")
|
||||
|
||||
def run_integration_tests():
|
||||
"""Run all integration tests and generate report"""
|
||||
print("=" * 70)
|
||||
print("NetAlertX SQL Injection Fix - Integration Test Suite")
|
||||
print("Validating PR #1182 as requested by maintainer jokob-sk")
|
||||
print("=" * 70)
|
||||
|
||||
# Run tests
|
||||
suite = unittest.TestLoader().loadTestsFromTestCase(NetAlertXIntegrationTest)
|
||||
runner = unittest.TextTestRunner(verbosity=2)
|
||||
result = runner.run(suite)
|
||||
|
||||
# Generate summary
|
||||
print("\n" + "=" * 70)
|
||||
print("INTEGRATION TEST SUMMARY")
|
||||
print("=" * 70)
|
||||
|
||||
total_tests = result.testsRun
|
||||
failures = len(result.failures)
|
||||
errors = len(result.errors)
|
||||
passed = total_tests - failures - errors
|
||||
|
||||
print(f"Total Tests: {total_tests}")
|
||||
print(f"Passed: {passed}")
|
||||
print(f"Failed: {failures}")
|
||||
print(f"Errors: {errors}")
|
||||
print(f"Success Rate: {(passed/total_tests)*100:.1f}%")
|
||||
|
||||
if failures == 0 and errors == 0:
|
||||
print("\n🎉 ALL INTEGRATION TESTS PASSED!")
|
||||
print("✅ Ready for maintainer approval")
|
||||
return True
|
||||
else:
|
||||
print("\n❌ INTEGRATION TESTS FAILED")
|
||||
print("🚫 Requires fixes before approval")
|
||||
return False
|
||||
assert 'new_devices' in result
|
||||
assert 'events' in result
|
||||
assert 'new_devices_meta' in result
|
||||
assert 'events_meta' in result
|
||||
assert mock_db.get_table_as_json.called
|
||||
|
||||
if __name__ == "__main__":
|
||||
success = run_integration_tests()
|
||||
sys.exit(0 if success else 1)
|
||||
def test_notification_system_integration(builder):
|
||||
email_condition = "AND devName = 'EmailTestDevice'"
|
||||
condition, params = builder.get_safe_condition_legacy(email_condition)
|
||||
assert "devName = :" in condition
|
||||
assert 'EmailTestDevice' in params.values()
|
||||
|
||||
apprise_condition = "AND eve_EventType = 'Connected'"
|
||||
condition, params = builder.get_safe_condition_legacy(apprise_condition)
|
||||
assert "eve_EventType = :" in condition
|
||||
assert 'Connected' in params.values()
|
||||
|
||||
webhook_condition = "AND devComments LIKE '%webhook%'"
|
||||
condition, params = builder.get_safe_condition_legacy(webhook_condition)
|
||||
assert "devComments LIKE :" in condition
|
||||
assert '%webhook%' in params.values()
|
||||
|
||||
mqtt_condition = "AND eve_MAC = 'aa:bb:cc:dd:ee:ff'"
|
||||
condition, params = builder.get_safe_condition_legacy(mqtt_condition)
|
||||
assert "eve_MAC = :" in condition
|
||||
assert 'aa:bb:cc:dd:ee:ff' in params.values()
|
||||
|
||||
def test_settings_persistence(builder):
|
||||
test_settings = [
|
||||
"AND devName = 'Persistent Device'",
|
||||
"AND devComments = {s-quote}Legacy Quote{s-quote}",
|
||||
"AND eve_EventType IN ('Connected', 'Disconnected')",
|
||||
"AND devLastIP = '192.168.1.1'",
|
||||
""
|
||||
]
|
||||
for setting in test_settings:
|
||||
condition, params = builder.get_safe_condition_legacy(setting)
|
||||
assert isinstance(condition, str)
|
||||
assert isinstance(params, dict)
|
||||
|
||||
def test_device_operations(builder):
|
||||
device_conditions = [
|
||||
"AND devName = 'Updated Device'",
|
||||
"AND devMac = 'aa:bb:cc:dd:ee:ff'",
|
||||
"AND devComments = 'Device updated successfully'",
|
||||
"AND devLastIP = '192.168.1.200'"
|
||||
]
|
||||
for cond in device_conditions:
|
||||
safe_condition, params = builder.get_safe_condition_legacy(cond)
|
||||
assert len(params) > 0 or safe_condition == ""
|
||||
assert "'" not in safe_condition
|
||||
|
||||
def test_plugin_functionality(builder):
|
||||
plugin_conditions = [
|
||||
"AND Plugin = 'TestPlugin'",
|
||||
"AND Object_PrimaryId = 'primary123'",
|
||||
"AND Status = 'Active'"
|
||||
]
|
||||
for cond in plugin_conditions:
|
||||
safe_condition, params = builder.get_safe_condition_legacy(cond)
|
||||
if safe_condition:
|
||||
assert ":" in safe_condition
|
||||
assert len(params) > 0
|
||||
|
||||
def test_sql_injection_prevention(builder):
|
||||
malicious_inputs = [
|
||||
"'; DROP TABLE Events_Devices; --",
|
||||
"' OR '1'='1",
|
||||
"1' UNION SELECT * FROM Devices --",
|
||||
"'; INSERT INTO Events VALUES ('hacked'); --",
|
||||
"' AND (SELECT COUNT(*) FROM sqlite_master) > 0 --"
|
||||
]
|
||||
for payload in malicious_inputs:
|
||||
condition, params = builder.get_safe_condition_legacy(payload)
|
||||
assert condition == ""
|
||||
assert params == {}
|
||||
|
||||
def test_error_handling(builder):
|
||||
invalid_condition = "INVALID SQL SYNTAX HERE"
|
||||
condition, params = builder.get_safe_condition_legacy(invalid_condition)
|
||||
assert condition == ""
|
||||
assert params == {}
|
||||
|
||||
edge_cases = [None, "", " ", "\n\t", "AND column_not_in_whitelist = 'value'"]
|
||||
for case in edge_cases:
|
||||
if case is not None:
|
||||
condition, params = builder.get_safe_condition_legacy(case)
|
||||
assert isinstance(condition, str)
|
||||
assert isinstance(params, dict)
|
||||
|
||||
def test_backward_compatibility(builder):
|
||||
legacy_conditions = [
|
||||
"AND devName = {s-quote}Legacy Device{s-quote}",
|
||||
"AND devComments = {s-quote}Old Style Quote{s-quote}",
|
||||
"AND devName = 'Normal Quote'"
|
||||
]
|
||||
for cond in legacy_conditions:
|
||||
condition, params = builder.get_safe_condition_legacy(cond)
|
||||
if condition:
|
||||
assert "{s-quote}" not in condition
|
||||
assert ":" in condition
|
||||
assert len(params) > 0
|
||||
|
||||
def test_performance_impact(builder):
|
||||
import time
|
||||
test_condition = "AND devName = 'Performance Test Device'"
|
||||
start = time.time()
|
||||
for _ in range(1000):
|
||||
condition, params = builder.get_safe_condition_legacy(test_condition)
|
||||
end = time.time()
|
||||
avg_ms = (end - start) / 1000 * 1000
|
||||
assert avg_ms < 1.0
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script to validate SQL injection fixes for issue #1179
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
|
||||
def test_datetime_injection_fix():
|
||||
"""Test that datetime injection vulnerability is fixed"""
|
||||
|
||||
# Read the reporting.py file
|
||||
with open('server/messaging/reporting.py', 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Check for vulnerable f-string patterns with datetime and user input
|
||||
vulnerable_patterns = [
|
||||
r"datetime\('now',\s*f['\"].*{get_setting_value\('NTFPRCS_alert_down_time'\)}",
|
||||
r"datetime\('now',\s*f['\"].*{get_timezone_offset\(\)}"
|
||||
]
|
||||
|
||||
vulnerabilities_found = []
|
||||
for pattern in vulnerable_patterns:
|
||||
matches = re.findall(pattern, content)
|
||||
if matches:
|
||||
vulnerabilities_found.extend(matches)
|
||||
|
||||
if vulnerabilities_found:
|
||||
print("❌ SECURITY TEST FAILED: Vulnerable datetime patterns found:")
|
||||
for vuln in vulnerabilities_found:
|
||||
print(f" - {vuln}")
|
||||
return False
|
||||
|
||||
# Check for the secure patterns
|
||||
secure_patterns = [
|
||||
r"minutes = int\(get_setting_value\('NTFPRCS_alert_down_time'\) or 0\)",
|
||||
r"tz_offset = get_timezone_offset\(\)"
|
||||
]
|
||||
|
||||
secure_found = 0
|
||||
for pattern in secure_patterns:
|
||||
if re.search(pattern, content):
|
||||
secure_found += 1
|
||||
|
||||
if secure_found >= 2:
|
||||
print("✅ SECURITY TEST PASSED: Secure datetime handling implemented")
|
||||
return True
|
||||
else:
|
||||
print("⚠️ SECURITY TEST WARNING: Expected secure patterns not fully found")
|
||||
return False
|
||||
|
||||
def test_notification_instance_fix():
|
||||
"""Test that the clearPendingEmailFlag function is secure"""
|
||||
|
||||
with open('server/models/notification_instance.py', 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Check for vulnerable f-string patterns in clearPendingEmailFlag
|
||||
clearflag_section = ""
|
||||
in_function = False
|
||||
lines = content.split('\n')
|
||||
|
||||
for line in lines:
|
||||
if 'def clearPendingEmailFlag' in line:
|
||||
in_function = True
|
||||
elif in_function and line.strip() and not line.startswith(' ') and not line.startswith('\t'):
|
||||
break
|
||||
|
||||
if in_function:
|
||||
clearflag_section += line + '\n'
|
||||
|
||||
# Check for vulnerable patterns
|
||||
vulnerable_patterns = [
|
||||
r"f['\"].*{get_setting_value\('NTFPRCS_alert_down_time'\)}",
|
||||
r"f['\"].*{get_timezone_offset\(\)}"
|
||||
]
|
||||
|
||||
vulnerabilities_found = []
|
||||
for pattern in vulnerable_patterns:
|
||||
matches = re.findall(pattern, clearflag_section)
|
||||
if matches:
|
||||
vulnerabilities_found.extend(matches)
|
||||
|
||||
if vulnerabilities_found:
|
||||
print("❌ SECURITY TEST FAILED: clearPendingEmailFlag still vulnerable:")
|
||||
for vuln in vulnerabilities_found:
|
||||
print(f" - {vuln}")
|
||||
return False
|
||||
|
||||
print("✅ SECURITY TEST PASSED: clearPendingEmailFlag appears secure")
|
||||
return True
|
||||
|
||||
def test_code_quality():
|
||||
"""Test basic code quality and imports"""
|
||||
|
||||
# Check if the modified files can be imported (basic syntax check)
|
||||
try:
|
||||
import subprocess
|
||||
result = subprocess.run([
|
||||
'python3', '-c',
|
||||
'import sys; sys.path.append("server"); from messaging import reporting'
|
||||
], capture_output=True, text=True, cwd='.')
|
||||
|
||||
if result.returncode == 0:
|
||||
print("✅ CODE QUALITY TEST PASSED: reporting.py imports successfully")
|
||||
return True
|
||||
else:
|
||||
print(f"❌ CODE QUALITY TEST FAILED: Import error: {result.stderr}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"⚠️ CODE QUALITY TEST WARNING: Could not test imports: {e}")
|
||||
return True # Don't fail for environment issues
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("🔒 Running SQL Injection Security Tests for Issue #1179\n")
|
||||
|
||||
tests = [
|
||||
("Datetime Injection Fix", test_datetime_injection_fix),
|
||||
("Notification Instance Security", test_notification_instance_fix),
|
||||
("Code Quality", test_code_quality)
|
||||
]
|
||||
|
||||
results = []
|
||||
for test_name, test_func in tests:
|
||||
print(f"Running: {test_name}")
|
||||
result = test_func()
|
||||
results.append(result)
|
||||
print()
|
||||
|
||||
passed = sum(results)
|
||||
total = len(results)
|
||||
|
||||
print(f"🔒 Security Test Summary: {passed}/{total} tests passed")
|
||||
|
||||
if passed == total:
|
||||
print("✅ All security tests passed! The SQL injection fixes are working correctly.")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("❌ Some security tests failed. Please review the fixes.")
|
||||
sys.exit(1)
|
||||
Reference in New Issue
Block a user