Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8f7f7ea
BE: Trigger failing if object non-existent + emoji removal in upgrade…
jokob-sk May 9, 2026
300820e
Merge pull request #1633 from netalertx/main
jokob-sk May 9, 2026
1def218
Update README and enhance device management logic
jokob-sk May 9, 2026
9575692
Fix unsupported object format handling in UpdateFieldAction and Delet…
jokob-sk May 9, 2026
f428f45
Update README to include Domotz as a network monitoring option and re…
jokob-sk May 9, 2026
556d104
Merge pull request #1638 from netalertx/main
jokob-sk May 12, 2026
f11a63b
BE: DIGSCAN could not be disbaled due to default RUN set in config.js…
jokob-sk May 12, 2026
13f8858
BE: Less verbose SYNC plugin #1164
jokob-sk May 12, 2026
0127194
Merge branch 'next_release' of github.com:netalertx/NetAlertX into ne…
jokob-sk May 14, 2026
4cac81b
PLG: ICMP fping fix for multiple interfaces #1642
jokob-sk May 14, 2026
6e149f8
Merge branch 'next_release' of github.com:netalertx/NetAlertX into ne…
jokob-sk May 14, 2026
4761a68
Merge pull request #1643 from netalertx/main
jokob-sk May 14, 2026
f6b3484
BE: ICMP fping with multiple interfaces #1642
jokob-sk May 14, 2026
09744f3
Merge branch 'next_release' of github.com:netalertx/NetAlertX into ne…
jokob-sk May 15, 2026
7725697
BE: ICMP fping with multiple interfaces #1642
jokob-sk May 15, 2026
92adadd
Merge branch 'next_release' of github.com:netalertx/NetAlertX into ne…
jokob-sk May 16, 2026
bf4e0b4
BE+FE: timestamps for sessions and emails corrected #1639
jokob-sk May 16, 2026
47a5591
Merge branch 'next_release' of github.com:netalertx/NetAlertX into ne…
jokob-sk May 16, 2026
292223e
BE+FE: timestamps for sessions and emails corrected #1639
jokob-sk May 16, 2026
af14dea
Merge branch 'next_release' of github.com:netalertx/NetAlertX into ne…
jokob-sk May 16, 2026
35dc9f9
front/lib/moment/moment.js: Avoid loading path-looking locales from fs
npt-1707 May 17, 2026
0517da2
front/lib/moment/moment.js: fix redos using local backtracking regex
npt-1707 May 17, 2026
80c8a66
front/lib/datatables/datatables.js: Fix XSS in Alert, Carousel, Colla…
npt-1707 May 17, 2026
328e591
Merge pull request #1645 from npt-1707/fix_CVE-2016-10735
jokob-sk May 18, 2026
f0684c6
Merge pull request #1646 from npt-1707/fix_CVE-2023-22467
jokob-sk May 18, 2026
9eaaf50
Merge pull request #1647 from npt-1707/fix_CVE-2022-24785
jokob-sk May 18, 2026
bc87e39
Enhance documentation and improve formatting across multiple files
jokob-sk May 18, 2026
6580c4a
Merge branch 'next_release' of https://github.com/netalertx/NetAlertX…
jokob-sk May 18, 2026
5f62d25
Implement XSS prevention by encoding special characters in device nam…
jokob-sk May 21, 2026
821594f
Refine documentation and enhance XSS prevention measures in device de…
jokob-sk May 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ Check the [GitHub Issues](https://github.com/netalertx/NetAlertX/issues) for the
## Everything else
<!--- --------------------------------------------------------------------- --->

<a href="https://trendshift.io/repositories/12670" target="_blank"><img src="https://trendshift.io/api/badge/repositories/12670" alt="jokob-sk%2FNetAlertX | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
<a href="https://trendshift.io/repositories/19712" target="_blank"><img src="https://trendshift.io/api/badge/repositories/19712" alt="jokob-sk%2FNetAlertX | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>

### 📧 Get notified what's new

Expand All @@ -170,6 +170,7 @@ Get notified about a new release, what new functionality you can use and about b
- [Fing](https://www.fing.com/) - Network scanner app for your Internet security (Commercial, Phone App, Proprietary hardware)
- [NetBox](https://netboxlabs.com/) - The gold standard for Network Source of Truth (NSoT) and IPAM.
- [Zabbix](https://www.zabbix.com/) or [Nagios](https://www.nagios.org/) - Strong focus on infrastructure monitoring.
- [Domotz](https://www.domotz.com/) - Commercial network monitoring and remote management platform aimed at MSPs, IT teams, and multi-site environments.
- [NetAlertX](https://netalertx.com) - The streamlined, discovery-focused choice for real-time asset intelligence and noise-free alerting.

### 💙 Donations
Expand Down
1 change: 1 addition & 0 deletions back/app.conf
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ REPORT_DASHBOARD_URL='update_REPORT_DASHBOARD_URL_setting'
INTRNT_RUN='schedule'
ARPSCAN_RUN='schedule'
NSLOOKUP_RUN='before_name_updates'
DIGSCAN_RUN='before_name_updates'
AVAHISCAN_RUN='before_name_updates'
NBTSCAN_RUN='before_name_updates'

Expand Down
3 changes: 3 additions & 0 deletions docs/ADVISORY_EYES_ON_GLASS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ For Managed Service Providers (MSPs) and Network Operations Centers (NOC), "Eyes

![filters](./img/ADVISORIES/filters.png)

> [!TIP]
> If you are using Grafana check the `/metrics` endpoint that exposes **Prometheus-compatible metrics** for NetAlertX, including aggregate device counts and per-device status. See the [Metrics API endpoint](./API_METRICS.md) documentation for details.

---

### 1. Configure Auto-Refresh for Live Monitoring
Expand Down
18 changes: 12 additions & 6 deletions docs/ADVISORY_MULTI_NETWORK.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,25 @@ Effective multi-network monitoring starts with understanding how NetAlertX "sees
* **Manual Entry:** For static assets where only ICMP (ping) status is needed.

> [!TIP]
> Explore the [remote networks](./REMOTE_NETWORKS.md) documentation for more details on how to set up the approaches menationed above.
> Explore the [remote networks](./REMOTE_NETWORKS.md) documentation for more details on how to set up the approaches mentioned above.

---

### 2. Automating IT Asset Inventory with Workflows

[Workflows](./WORKFLOWS.md) are the "engine" of NetAlertX, reducing manual overhead as your device list grows.
[Workflows](./WORKFLOWS.md) are the "engine" of NetAlertX, reducing manual overhead as your device list grows. See some examples below:

#### A. Logical Ownership & VLAN Tagging

Create a workflow triggered on **Device Creation** to:

* **A. Logical Ownership & VLAN Tagging:** Create a workflow triggered on **Device Creation** to:
1. Inspect the IP/Subnet.
2. Set `devVlan` or `devOwner` custom fields automatically.

#### B. Auto-Grouping:

Use conditional logic to categorize devices.

* **B. Auto-Grouping:** Use conditional logic to categorize devices.
* *Example:* If `devLastIP == 10.10.20.*`, then `Set devLocation = "BranchOffice"`.

```json
Expand Down Expand Up @@ -56,9 +61,9 @@ Effective multi-network monitoring starts with understanding how NetAlertX "sees
]
}
```
#### C. Sync Node Tracking


* **C. Sync Node Tracking:** When using multiple instances, ensure all synchub nodes have a descriptive `SYNC_node_name` name to distinguish between sites.
When using multiple instances, ensure all sync hub nodes have a descriptive `SYNC_node_name` name to distinguish between sites.

> [!TIP]
> Always test new workflows in a "Staging" instance. A misconfigured workflow can trigger thousands of unintended updates across your database.
Expand Down Expand Up @@ -107,6 +112,7 @@ As your environment grows, tuning the underlying engine is vital to maintain a s
* **Plugin Scheduling:** Avoid "Scan Storms" by staggering plugin execution. Running intensive tasks like `NMAP` or `MASS_DNS` simultaneously can spike CPU and cause database locks.
* **Database Health:** Large-scale monitoring generates massive event logs. Use the **[DBCLNP (Database Cleanup)](https://www.google.com/search?q=https://docs.netalertx.com/PLUGINS/%23dbclnp)** plugin to prune old records and keep the SQLite database performant.
* **Resource Management:** For high-device counts, consider increasing the memory limit for the container and utilizing `tmpfs` for temporary files to reduce SD card/disk I/O bottlenecks.
* Enable the `DEEP_SLEEP` setting.

> [!IMPORTANT]
> For a deep dive into hardware requirements, database vacuuming, and specific environment variables for high-load instances, refer to the full **[Performance Optimization Guide](https://docs.netalertx.com/PERFORMANCE/)**.
Expand Down
2 changes: 2 additions & 0 deletions docs/PLUGINS_DEV_UI_COMPONENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Configure how your plugin's data is displayed in the NetAlertX web interface.
Plugin results are displayed in the UI via the **Plugins page** and **Device details tabs**. You control the appearance and functionality of these displays by defining `database_column_definitions` in your plugin's `config.json`.

Each column definition specifies:

- Which data field to display
- How to render it (label, link, color-coded badge, etc.)
- What CSS classes to apply
Expand Down Expand Up @@ -275,6 +276,7 @@ Replaces specific values with display strings or HTML.
```

**Output Examples:**

- `"online"` → 🟢 Online
- `"offline"` → 🔴 Offline
- `"idle"` → 🟡 Idle
Expand Down
28 changes: 14 additions & 14 deletions docs/WORKFLOW_EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,22 +45,22 @@ Sometimes devices are manually archived (e.g., no longer expected on the network
],
"enabled": "Yes"
}
```
````

### 🔍 Explanation

- Trigger: Listens for updates to device records.
- Conditions:
- `devIsArchived` is `1` (archived).
- `devPresentLastScan` is `1` (device was detected in the latest scan).
- Action: Updates the device to set `devIsArchived` to `0` (unarchived).
* **Trigger**: Listens for updates to device records.
* **Conditions**:

### ✅ Result
* `devIsArchived` is `1` (archived).
* `devPresentLastScan` is `1` (device was detected in the latest scan).
* **Action**:

Whenever a previously archived device shows up during a network scan, it will be automatically unarchived — allowing it to reappear in your device lists and dashboards.
* Updates the device to set `devIsArchived` to `0` (unarchived).

### ✅ Result

Here is your updated version of **Example 2** and **Example 3**, fully aligned with the format and structure of **Example 1** for consistency and professionalism:
Whenever a previously archived device shows up during a network scan, it will be automatically unarchived — allowing it to reappear in your device lists and dashboards.

---

Expand Down Expand Up @@ -107,7 +107,7 @@ When new devices join your network, assigning them to the correct network node i
### 🔍 Explanation

* **Trigger**: Activates when a new device is added.
* **Condition**:
* **Conditions**:

* `devLastIP` contains `192.168.1.` (matches subnet).
* **Action**:
Expand Down Expand Up @@ -173,12 +173,12 @@ You may want to automatically clear out newly detected Google devices (such as C
* **Trigger**: Runs on device updates.
* **Conditions**:

* Vendor contains `Google`.
* Device is marked as new (`devIsNew` is `1`).
* `devVendor` contains `Google`.
* `devIsNew` is `1` (device marked as new).
* **Actions**:

1. Set `devIsNew` to `0` (mark as not new).
2. Delete the device.
1. Sets `devIsNew` to `0` (mark as not new).
2. Deletes the device.

### ✅ Result

Expand Down
1 change: 1 addition & 0 deletions front/css/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -1417,6 +1417,7 @@ textarea[readonly],
grid-template-columns: repeat(auto-fit, minmax(125px, 1fr));
gap: 0.75em;
padding: 0.5em 0;
padding-top: 0;
}

#columnFilters::before,
Expand Down
81 changes: 57 additions & 24 deletions front/deviceDetails.php
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,10 @@ function updateChevrons(currentMac) {
pos = refreshedList.findIndex(item => item.devMac === currentMac);

if (pos === -1) {
console.error('Still not found after re-cache:', currentMac);
console.warn('Device not found in device list after re-cache — hiding navigation controls:', currentMac);
$('#txtRecord').hide();
$('#btnPrevious').hide();
$('#btnNext').hide();
return;
}

Expand Down Expand Up @@ -499,26 +502,9 @@ function initializeTabs () {
}
}

function updateDevicePageName(mac) {
let name = getDevDataByMac(mac, "devName");
let owner = getDevDataByMac(mac, "devOwner");

// If data is missing, re-cache and retry once
if (mac != 'new' && (name === null|| owner === null)) {
console.warn("Device not found in cache, retrying after re-cache:", mac);
showSpinner();
cacheDevices(true).then(() => {
hideSpinner();
// Retry after successful cache
updateDevicePageName(mac);
}).catch((err) => {
hideSpinner();
console.error("Failed to refresh devices:", err);
});
return; // Exit early to avoid showing bad data
}

// Page title - Name
// ----------------------------------------
// Write device name/owner into page title DOM. Pure DOM side-effect, no data fetching.
function applyDevicePageTitle(mac, name, owner) {
let pageTitleText;

if (mac === "new") {
Expand All @@ -530,12 +516,12 @@ function updateDevicePageName(mac) {
`<i class="fa fa-circle-info"></i> ` + getString("Gen_create_new_device_info")
);
$('#devicePageInfoPlc').show();
} else if (!owner || name.toString().includes(owner)) {
pageTitleText = name;
} else if (!owner || (name && name.toString().includes(owner))) {
pageTitleText = encodeSpecialChars(name ?? getString("DevDetail_EveandAl_NewDevice"));
$('#pageTitle').html(pageTitleText);
$('#devicePageInfoPlc').hide();
} else {
pageTitleText = `${name} (${owner})`;
pageTitleText = `${encodeSpecialChars(name ?? getString("DevDetail_EveandAl_NewDevice"))} (${encodeSpecialChars(owner)})`;
$('#pageTitle').html(pageTitleText);
$('#devicePageInfoPlc').hide();
}
Expand All @@ -544,6 +530,53 @@ function updateDevicePageName(mac) {
$('title').html(pageTitleText + ' - ' + $('title').html());
}

// ----------------------------------------
// Resolve device name/owner for the page title.
// Stage 1: localStorage cache (synchronous, fast path).
// Stage 2: one forced re-cache from table_devices.json.
// Stage 3: REST API fallback so a direct-link visit never loops.
async function updateDevicePageName(mac) {
let name = getDevDataByMac(mac, "devName");
let owner = getDevDataByMac(mac, "devOwner");

// Stage 2: one re-cache attempt
if (mac !== 'new' && name === null) {
console.warn("Device not in cache, attempting re-cache:", mac);
showSpinner();
try {
await cacheDevices(true);
} catch (err) {
console.error("Re-cache failed:", err);
} finally {
hideSpinner();
}
name = getDevDataByMac(mac, "devName");
owner = getDevDataByMac(mac, "devOwner");
}

// Stage 3: REST fallback — same endpoint renderSmallBoxes uses, always DB-direct
if (mac !== 'new' && name === null) {
console.warn("Device not found in cache after re-cache, falling back to REST API:", mac);
try {
const { apiBase, authHeader } = getAuthContext();
const res = await fetch(`${apiBase}/device/${encodeURIComponent(mac)}`, {
headers: authHeader
});
if (res.ok) {
const data = await res.json();
name = data.devName ?? null;
owner = data.devOwner ?? null;
} else {
console.error("REST fallback for device name returned:", res.status);
}
} catch (err) {
console.error("REST fallback error:", err);
}
}

applyDevicePageTitle(mac, name, owner);
}


//-----------------------------------------------------------------------------------

Expand Down
6 changes: 6 additions & 0 deletions front/deviceDetailsEdit.php
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ function getDeviceData() {
// console.log(setting.setKey);
// console.log(fieldData);


// Additional form elements like the random MAC address button for devMac
let inlineControl = "";
// handle random mac
Expand Down Expand Up @@ -329,6 +330,11 @@ function getDeviceData() {
fieldOptionsOverride = fieldDataNew;
}

// XSS prevention - encode special characters for string fields, but not for arrays (like children dynamic)
// Don't move above the handle devChildrenDynamic block because it relies on the original fieldData to generate options
fieldData = encodeSpecialChars(fieldData);
// console.log(fieldData);

Comment thread
coderabbitai[bot] marked this conversation as resolved.
// Generate the input field HTML
const inputFormHtml = `<div class="form-group col-xs-12">
<label id="${setting.setKey}_label" class="${obj.labelClasses}" > ${setting.setName}
Expand Down
3 changes: 0 additions & 3 deletions front/deviceDetailsSessions.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,6 @@ function initializeSessionsDatatable (sessionsRows) {

if (!cellData.includes("missing event") && !cellData.includes("..."))
{
if (cellData.includes("+")) { // Check if timezone offset is present
cellData = cellData.split('+')[0]; // Remove timezone offset
}
// console.log(cellData);
result = localizeTimestamp(cellData);
} else
Expand Down
61 changes: 31 additions & 30 deletions front/devices.php
Original file line number Diff line number Diff line change
Expand Up @@ -883,40 +883,41 @@ function initializeDatatable (status) {
{orderData: [mapIndx(COL.devIpLong)], targets: mapIndx(COL.devLastIP) },

// Device Name and FQDN
// Use `render` (not `createdCell`) so the HTML is built before DataTables
// sets td.innerHTML – preventing raw cellData from being parsed as HTML.
{targets: [mapIndx(COL.devName), mapIndx(COL.devFQDN)],
'createdCell': function (td, cellData, rowData, row, col) {

// console.log(cellData)
'render': function (data, type, row) {
if (type !== 'display') {
return data; // raw value for sort / filter / type detection
}

var displayedValue = cellData;
var displayedValue = encodeSpecialChars(data);

if(isEmpty(displayedValue))
{
displayedValue = "N/A"
if (isEmpty(displayedValue)) {
displayedValue = "N/A";
}
$(td).html (
`<b class="anonymizeDev "
>
<a href="deviceDetails.php?mac=${rowData[mapIndx(COL.devMac)]}" class="hover-node-info"
data-name="${displayedValue}"
data-ip="${rowData[mapIndx(COL.devLastIP)]}"
data-mac="${rowData[mapIndx(COL.devMac)]}"
data-vendor="${rowData[mapIndx(COL.devVendor)]}"
data-type="${rowData[mapIndx(COL.devType)]}"
data-firstseen="${rowData[mapIndx(COL.devFirstConnection)]}"
data-lastseen="${rowData[mapIndx(COL.devLastConnection)]}"
data-relationship="${rowData[mapIndx(COL.devParentRelType)]}"
data-status="${rowData[mapIndx(COL.devStatus)]}"
data-present="${rowData[mapIndx(COL.devPresentLastScan)]}"
data-alertdown="${rowData[mapIndx(COL.devAlertDown)]}"
data-flapping="${rowData[mapIndx(COL.devFlapping)]}"
data-sleeping="${rowData[COL_EXTRA.devIsSleeping] || 0}"
data-archived="${rowData[COL_EXTRA.devIsArchived] || 0}"
data-isnew="${rowData[COL_EXTRA.devIsNew] || 0}"
data-icon="${rowData[mapIndx(COL.devIcon)]}">
${displayedValue}
</a>
</b>`

return (
`<b class="anonymizeDev ">` +
`<a href="deviceDetails.php?mac=${row[mapIndx(COL.devMac)]}" class="hover-node-info"` +
` data-name="${displayedValue}"` +
` data-ip="${row[mapIndx(COL.devLastIP)]}"` +
` data-mac="${row[mapIndx(COL.devMac)]}"` +
` data-vendor="${row[mapIndx(COL.devVendor)]}"` +
` data-type="${row[mapIndx(COL.devType)]}"` +
` data-firstseen="${row[mapIndx(COL.devFirstConnection)]}"` +
` data-lastseen="${row[mapIndx(COL.devLastConnection)]}"` +
` data-relationship="${row[mapIndx(COL.devParentRelType)]}"` +
` data-status="${row[mapIndx(COL.devStatus)]}"` +
` data-present="${row[mapIndx(COL.devPresentLastScan)]}"` +
` data-alertdown="${row[mapIndx(COL.devAlertDown)]}"` +
` data-flapping="${row[mapIndx(COL.devFlapping)]}"` +
` data-sleeping="${row[COL_EXTRA.devIsSleeping] || 0}"` +
` data-archived="${row[COL_EXTRA.devIsArchived] || 0}"` +
` data-isnew="${row[COL_EXTRA.devIsNew] || 0}"` +
` data-icon="${row[mapIndx(COL.devIcon)]}"` +
`>${displayedValue}</a>` +
Comment thread
jokob-sk marked this conversation as resolved.
`</b>`
);
} },

Expand Down
8 changes: 5 additions & 3 deletions front/events.php
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,11 @@ function initializeDatatable() {
{ targets: [0,5,6,7,8,10,11,12,13], visible: false },
{ targets: [7], orderData: [8] },
{ targets: [9], orderData: [10] },
{ targets: [1], createdCell: (td, cellData, rowData) => {
// Device column as link
$(td).html(`<b><a href="deviceDetails.php?mac=${rowData[13]}">${cellData}</a></b>`);
// Use `render` (not `createdCell`) so encodeSpecialChars runs before
// DataTables sets td.innerHTML, preventing devName XSS execution.
{ targets: [1], render: function (data, type, row) {
if (type !== 'display') { return data; }
return `<b><a href="deviceDetails.php?mac=${row[13]}">${encodeSpecialChars(data)}</a></b>`;
}},
Comment thread
jokob-sk marked this conversation as resolved.
{ targets: [3], createdCell: (td, cellData) => $(td).html(localizeTimestamp(cellData)) },
{ targets: [4,5,6,7], createdCell: (td, cellData) => $(td).html(translateHTMLcodes(cellData)) }
Expand Down
Loading
Loading