Security fixes and schema cleanup

- Fix SQL injection in displayprofile.asp (parameterized query)
- Add HTMLEncode to XSS-vulnerable output in 5 display pages
- Add Option Explicit to computers.asp, displaymachines.asp, displaypcs.asp, displayapplication.asp, displayprofile.asp
- Update STANDARDS.md with test script reference, secrets management, column naming gotchas
- Fix equipment type ranges in CLAUDE.md and QUICK_REFERENCE.md (1-15, 21-25)
- Add migration SQL to cleanup redundant PC machinetypes (34-46)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2025-12-12 07:22:16 -05:00
parent 693789138d
commit e0d89f9957
9 changed files with 258 additions and 57 deletions

View File

@@ -36,8 +36,8 @@ For production tasks, user must:
```
machines table (unified)
├── Equipment (machinetypeid 1-24, pctypeid IS NULL)
├── PCs (machinetypeid 33-35, pctypeid IS NOT NULL)
├── Equipment (machinetypeid 1-15 and 21-25, pctypeid IS NULL)
├── PCs (machinetypeid 33+, pctypeid IS NOT NULL)
└── Network Devices (machinetypeid 16-20)
printers table (separate)
@@ -51,8 +51,9 @@ machinerelationships table (PC↔equipment links)
-- All PCs
SELECT * FROM machines WHERE pctypeid IS NOT NULL;
-- All Equipment
SELECT * FROM machines WHERE pctypeid IS NULL AND machinetypeid < 16;
-- All Equipment (excludes network devices and PCs)
SELECT * FROM machines WHERE pctypeid IS NULL
AND machinetypeid NOT IN (16,17,18,19,20) AND machinetypeid < 33;
-- Network devices
SELECT * FROM machines WHERE machinetypeid IN (16,17,18,19,20);

View File

@@ -1,3 +1,8 @@
<%@ Language=VBScript %>
<%
Option Explicit
Dim theme, strSQL, rs, objConn
%>
<!DOCTYPE html>
<html lang="en">
<head>
@@ -6,7 +11,6 @@
</head>
<%
' Force IIS recompile - timestamp: 20251110-143600
theme = Request.Cookies("theme")
IF theme = "" THEN
theme="bg-theme1"
@@ -61,7 +65,7 @@ While Not rsTypes.EOF
If CStr(rsTypes("pctypeid")) = CStr(currentPCType) Then
selectedAttr = " selected"
End If
Response.Write "<option value=""" & rsTypes("pctypeid") & """" & selectedAttr & ">" & rsTypes("typename") & "</option>" & vbCrLf
Response.Write "<option value=""" & rsTypes("pctypeid") & """" & selectedAttr & ">" & Server.HTMLEncode(rsTypes("typename") & "") & "</option>" & vbCrLf
rsTypes.MoveNext
Wend
rsTypes.Close
@@ -77,7 +81,7 @@ While Not rsStatus.EOF
If CStr(rsStatus("machinestatusid")) = CStr(currentPCStatus) Then
selectedAttr = " selected"
End If
Response.Write "<option value=""" & rsStatus("machinestatusid") & """" & selectedAttr & ">" & rsStatus("machinestatus") & "</option>" & vbCrLf
Response.Write "<option value=""" & rsStatus("machinestatusid") & """" & selectedAttr & ">" & Server.HTMLEncode(rsStatus("machinestatus") & "") & "</option>" & vbCrLf
rsStatus.MoveNext
Wend
rsStatus.Close
@@ -162,20 +166,20 @@ Set rsStatus = Nothing
while not rs.eof
%>
<td><a href="./displaypc.asp?machineid=<%Response.Write(rs("machineid"))%>" title="Click to Show PC Details"><%
<td><a href="./displaypc.asp?machineid=<%=rs("machineid")%>" title="Click to Show PC Details"><%
Dim displayName
If IsNull(rs("hostname")) Or rs("hostname") = "" Then
displayName = rs("serialnumber")
displayName = rs("serialnumber") & ""
Else
displayName = rs("hostname")
displayName = rs("hostname") & ""
End If
Response.Write(displayName)
Response.Write(Server.HTMLEncode(displayName))
%></a></td>
<td><%Response.Write(rs("serialnumber"))%></td>
<td><%Response.Write(rs("ipaddress"))%></td>
<td><%Response.Write(rs("modelnumber"))%></td>
<td><%Response.Write(rs("operatingsystem"))%></td>
<td><a href="./search.asp?search=<%Response.Write(rs("machinenumber"))%>" title="Click to Show Machine Details"><%Response.Write(rs("machinenumber"))%></td>
<td><%=Server.HTMLEncode(rs("serialnumber") & "")%></td>
<td><%=Server.HTMLEncode(rs("ipaddress") & "")%></td>
<td><%=Server.HTMLEncode(rs("modelnumber") & "")%></td>
<td><%=Server.HTMLEncode(rs("operatingsystem") & "")%></td>
<td><a href="./search.asp?search=<%=Server.URLEncode(rs("machinenumber") & "")%>" title="Click to Show Machine Details"><%=Server.HTMLEncode(rs("machinenumber") & "")%></a></td>
</tr>
<%

View File

@@ -1,6 +1,10 @@
<%@ Language=VBScript %>
<%
Option Explicit
%>
<!--#include file="./includes/sql.asp"-->
<%
Dim appid
Dim appid, rs
appid = Request.Querystring("appid")
' Basic validation - must be numeric and positive
@@ -59,10 +63,10 @@
<div class="col-lg-4">
<div class="card profile-card-2">
<div class="card-img-block">
<img class="img-fluid" src="./images/applications/<%Response.Write(rs("image"))%>" alt="Card image cap">
<img class="img-fluid" src="./images/applications/<%=Server.HTMLEncode(rs("image") & "")%>" alt="Card image cap">
</div>
<div class="card-body pt-5">
<img src="./images/applications/<%Response.Write(rs("image"))%>" alt="profile-image" class="profile">
<img src="./images/applications/<%=Server.HTMLEncode(rs("image") & "")%>" alt="profile-image" class="profile">
<h5 class="card-title"></h5>
<p class="card-text"><a href="" title="Click to Access Support Docs" target="_blank"></a></p>
</div>
@@ -85,7 +89,7 @@
</ul>
<div class="tab-content p-3">
<div class="tab-pane active" id="profile">
<h5 class="mb-3"><%Response.Write(rs("appname"))%></h5>
<h5 class="mb-3"><%=Server.HTMLEncode(rs("appname") & "")%></h5>
<div class="row">
<div class="col-md-3">
<p class="mb-2"><strong>Support Team:</strong></p>

View File

@@ -1,3 +1,8 @@
<%@ Language=VBScript %>
<%
Option Explicit
Dim theme, strSQL, rs, objConn
%>
<!DOCTYPE html>
<html lang="en">
<head>
@@ -105,21 +110,21 @@
Response.write("<tr>")
%>
<td>
<span class="location-link" data-machineid="<%Response.Write(rs("machineid"))%>" style="cursor:pointer;">
<span class="location-link" data-machineid="<%=rs("machineid")%>" style="cursor:pointer;">
<i class="zmdi zmdi-pin"></i>
</span>
</td>
<td><a href="./displaymachine.asp?machineid=<%Response.Write(rs("machineid"))%>" title="View Machine Details"><%
<td><a href="./displaymachine.asp?machineid=<%=rs("machineid")%>" title="View Machine Details"><%
Dim displayName
displayName = rs("machinenumber") & ""
If displayName = "" Then displayName = rs("hostname") & ""
If displayName = "" Then displayName = "ID:" & rs("machineid")
Response.Write(Server.HTMLEncode(displayName))
%></a></td>
<td><%Response.Write(rs("machinetype"))%></td>
<td><%Response.Write(rs("vendor"))%></td>
<td><%Response.Write(rs("modelnumber"))%></a></td>
<td><%Response.Write(rs("businessunit"))%></td>
<td><%=Server.HTMLEncode(rs("machinetype") & "")%></td>
<td><%=Server.HTMLEncode(rs("vendor") & "")%></td>
<td><%=Server.HTMLEncode(rs("modelnumber") & "")%></td>
<td><%=Server.HTMLEncode(rs("businessunit") & "")%></td>
</tr>

View File

@@ -1,3 +1,8 @@
<%@ Language=VBScript %>
<%
Option Explicit
Dim theme, strSQL, rs, displayName, uptimeDays, hasVnc, hasWinrm, vncHost, objConn
%>
<!DOCTYPE html>
<html lang="en">
<head>
@@ -6,7 +11,7 @@
</head>
<%
' displaypcs.asp - PC List Page (Phase 2 Schema) - Last Updated: 20251110-1440
' displaypcs.asp - PC List Page (Phase 2 Schema)
theme = Request.Cookies("theme")
IF theme = "" THEN
theme="bg-theme1"
@@ -148,7 +153,6 @@ Set rsStatus = Nothing
<%
' Build query based on filters
Dim pcStatusFilter, winrmFilterSQL, deviceTypeFilterSQL, pcTypeFilterSQL, uptimeFilterSQL, needsRelationshipFilter, whereClause
Dim displayName, hasVnc, vncHost, hasWinrm, uptimeDays
pcStatusFilter = Request.QueryString("pcstatus")
winrmFilterSQL = Request.QueryString("winrm")
deviceTypeFilterSQL = Request.QueryString("devicetype")
@@ -221,17 +225,17 @@ Set rsStatus = Nothing
%>
<tr>
<td><a href="./displaypc.asp?machineid=<%Response.Write(rs("machineid"))%>" title="Click to Show PC Details"><%
<td><a href="./displaypc.asp?machineid=<%=rs("machineid")%>" title="Click to Show PC Details"><%
If IsNull(rs("hostname")) Or rs("hostname") = "" Then
displayName = rs("serialnumber")
displayName = rs("serialnumber") & ""
Else
displayName = rs("hostname")
displayName = rs("hostname") & ""
End If
Response.Write(displayName)
Response.Write(Server.HTMLEncode(displayName))
%></a></td>
<td><%Response.Write(rs("serialnumber"))%></td>
<td><%Response.Write(rs("modelnumber"))%></td>
<td><%Response.Write(rs("operatingsystem"))%></td>
<td><%=Server.HTMLEncode(rs("serialnumber") & "")%></td>
<td><%=Server.HTMLEncode(rs("modelnumber") & "")%></td>
<td><%=Server.HTMLEncode(rs("operatingsystem") & "")%></td>
<td><%
' Equipment relationship column
If Not IsNull(rs("equipment_id")) And rs("equipment_id") <> "" Then

View File

@@ -1,3 +1,7 @@
<%@ Language=VBScript %>
<%
Option Explicit
%>
<!DOCTYPE html>
<html lang="en">
<head>
@@ -8,12 +12,19 @@
</head>
<%
Dim theme, sso, strSQL, rs, cmd
theme = Request.Cookies("theme")
IF theme = "" THEN
theme="bg-theme1"
END IF
' Validate SSO - must be numeric
sso = Request.Querystring("sso")
If Not IsNumeric(sso) Or sso = "" Then
sso = "1"
End If
sso = CLng(sso)
%>
<body class="bg-theme <%Response.Write(theme)%>">
@@ -45,20 +56,33 @@
<div class="card-img-block">
<%
' Use parameterized query to prevent SQL injection
Set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = objconn
cmd.CommandText = "SELECT * FROM employees WHERE SSO = ?"
cmd.CommandType = 1
cmd.Parameters.Append cmd.CreateParameter("@sso", 3, 1, , sso)
Set rs = cmd.Execute()
strSQL = "SELECT * from employees WHERE SSO="&sso
set rs = objconn.Execute(strSQL)
if rs.eof THEN
strSQL = "SELECT * from employees WHERE SSO=1"
set rs = objconn.Execute(strSQL)
END IF
If rs.EOF Then
' Default to SSO 1 if not found
rs.Close
Set rs = Nothing
Set cmd = Nothing
Set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = objconn
cmd.CommandText = "SELECT * FROM employees WHERE SSO = ?"
cmd.CommandType = 1
cmd.Parameters.Append cmd.CreateParameter("@sso", 3, 1, , 1)
Set rs = cmd.Execute()
End If
Set cmd = Nothing
%>
<img class="img-fluid" src="https://tsgwp00525.rd.ds.ge.com/EmployeeDBAPP/images/<%Response.Write(rs("Picture"))%>" alt="Card image cap">
<img class="img-fluid" src="https://tsgwp00525.rd.ds.ge.com/EmployeeDBAPP/images/<%=Server.HTMLEncode(rs("Picture") & "")%>" alt="Card image cap">
</div>
<div class="card-body pt-5">
<h5 class="card-title"><%Response.Write(rs("First_Name"))%>&nbsp;<%Response.Write(rs("Last_Name"))%></h5>
<h5 class="card-title"><%=Server.HTMLEncode(rs("First_Name") & "")%>&nbsp;<%=Server.HTMLEncode(rs("Last_Name") & "")%></h5>
</div>
<%
' Easter Egg for SSO 570005354
@@ -234,7 +258,7 @@ END IF
<h5 class="mb-3">Profile</h5>
<div class="row">
<div class="col-md-3">
<h6><%Response.Write(rs("First_Name"))%>&nbsp;<%Response.Write(rs("Last_Name"))%></h6>
<h6><%=Server.HTMLEncode(rs("First_Name") & "")%>&nbsp;<%=Server.HTMLEncode(rs("Last_Name") & "")%></h6>
<h6>SSO</h6>
<h6>Shift</h6>
<h6>Role</h6>
@@ -243,11 +267,11 @@ END IF
</div>
<div class="col-md-6">
<h6>&nbsp;<h6>
<h6><%Response.Write(rs("SSO"))%></h6>
<h6><%Response.Write(rs("shift"))%></h6>
<h6><%Response.Write(rs("Role"))%></h6>
<h6><%Response.Write(rs("Team"))%></h6>
<h6><%Response.Write(rs("Payno"))%></h6>
<h6><%=Server.HTMLEncode(rs("SSO") & "")%></h6>
<h6><%=Server.HTMLEncode(rs("shift") & "")%></h6>
<h6><%=Server.HTMLEncode(rs("Role") & "")%></h6>
<h6><%=Server.HTMLEncode(rs("Team") & "")%></h6>
<h6><%=Server.HTMLEncode(rs("Payno") & "")%></h6>
</div>
</div>
<!--/row-->

View File

@@ -39,9 +39,10 @@ machines (machineid, hostname, serialnumber, alias, machinenumber,
loggedinuser, machinenotes, isactive, maptop, mapleft, lastupdated)
-- Identify record type:
-- PCs: pctypeid IS NOT NULL (machinetypeid 33-35)
-- Equipment: pctypeid IS NULL AND machinetypeid < 16
-- PCs: pctypeid IS NOT NULL (machinetypeid 33+)
-- Equipment: pctypeid IS NULL AND machinetypeid NOT IN (16,17,18,19,20) AND machinetypeid < 33
-- Network Devices: machinetypeid IN (16,17,18,19,20)
-- Equipment types: 1-15, 21-25 (e.g., Lathe, Mill, CMM, Hobbing Machine, etc.)
```
### PC-Related Tables

View File

@@ -1,8 +1,8 @@
# Classic ASP Development Standards
## ShopDB Application
**Version:** 1.0
**Last Updated:** 2025-10-10
**Version:** 1.1
**Last Updated:** 2025-12-11
**Status:** MANDATORY for all new development and modifications
---
@@ -414,7 +414,7 @@ Standard error codes for user messaging:
' - machines (primary)
' - machinetypes, models, vendors, businessunits
' - printers (LEFT JOIN - may be NULL)
' - pc (LEFT JOIN - may be NULL)
' - communications (LEFT JOIN - may be NULL)
'
' SECURITY:
' - Requires authentication
@@ -691,9 +691,9 @@ Sub LogError(source, errorNum, errorDesc)
**Style:** lowercase, plural nouns
```sql
machines
machines -- Unified table: Equipment, PCs, Network Devices
printers
pc (exception - acronym)
communications -- Network interfaces (IP/MAC)
machinetypes
vendors
models
@@ -713,6 +713,23 @@ createdate
lastupdated
```
### Column Naming Gotchas
**IMPORTANT:** Be aware of these non-obvious column names:
| Expected | Actual | Table |
|----------|--------|-------|
| `ipaddress` | `address` | communications |
| `gateway` | `defaultgateway` | communications |
| `notes` | `machinenotes` | machines |
| `pcid` | `machineid` | machines (PCs are in unified table) |
| `pc_comm_config` | `commconfig` | (table name) |
| `pc_dnc_config` | `dncconfig` | (table name) |
**PC Identification:** PCs are in the `machines` table, identified by:
- `pctypeid IS NOT NULL`
- `machinetypeid IN (33, 34, 35)`
---
## Documentation Standards
@@ -904,6 +921,22 @@ Response.Write(RenderCachedUnitDropdown())
## Testing Standards
### Automated Form Testing
**REQUIRED:** Run the comprehensive test suite after making changes to ASP pages.
```bash
./tests/test_forms.sh
```
This script tests 41 endpoints including:
- Page load tests (dashboards, list views, maps)
- Add form page loads
- Form submissions (notifications, equipment, printers, subnets, applications, KB, vendors, models, network devices)
- API endpoints
Test data uses `AUTOTEST_` prefix for easy cleanup. See `tests/cleanup_test_data.sql`.
### Unit Testing
**REQUIRED:** Test all validation functions.
@@ -1021,6 +1054,20 @@ Before committing code, verify:
2. Never commit `config.asp` to source control
3. Add `config.asp` to `.gitignore`
### Secrets Management
**MANDATORY:** Store sensitive credentials in `secrets.md` (gitignored).
**Contents:**
- Zabbix API URL and token
- Gitea API URL and token
- Database credentials (per environment)
**NEVER commit:**
- API tokens
- Database passwords
- Authentication credentials
### Configuration Template
```vbscript
@@ -1216,6 +1263,7 @@ Call CleanupResources()
| Version | Date | Changes | Author |
|---------|------|---------|--------|
| 1.0 | 2025-10-10 | Initial standards document created from audit findings | Claude |
| 1.1 | 2025-12-11 | Updated for Phase 2 schema (unified machines table), added test script reference, secrets management, column naming gotchas | Claude |
---

View File

@@ -0,0 +1,110 @@
-- ============================================================================
-- ShopDB Database Migration: Cleanup Redundant PC Machinetypes
-- Date: 2025-12-12
-- Purpose: Remove redundant PC machinetypes (34-46) since pctypeid handles categorization
-- ============================================================================
--
-- CHANGES:
-- 1. Add primary key to installedapps table
-- 2. Migrate machines using PC-specific machinetypes to generic PC (33) + pctypeid
-- 3. Update models to use generic PC machinetype
-- 4. Remove unused PC machinetypes (34-43, 45-46), keep USB Device (44)
--
-- RUN ON: Production database
-- BACKUP FIRST: mysqldump -u root -p shopdb > shopdb_backup_$(date +%Y%m%d).sql
-- ============================================================================
-- Start transaction for safety
START TRANSACTION;
-- ============================================================================
-- 1. ADD PRIMARY KEY TO INSTALLEDAPPS TABLE
-- ============================================================================
ALTER TABLE installedapps
ADD COLUMN installedappid INT AUTO_INCREMENT PRIMARY KEY FIRST;
SELECT 'Added PK to installedapps' AS status;
-- ============================================================================
-- 2. MIGRATE MACHINES FROM PC-SPECIFIC TYPES TO GENERIC PC (33) + PCTYPEID
-- ============================================================================
-- PC - Standard (36) → machinetypeid=33, pctypeid=1 (Standard)
UPDATE machines
SET machinetypeid = 33, pctypeid = 1
WHERE machinetypeid = 36;
SELECT CONCAT('Migrated ', ROW_COUNT(), ' PC-Standard machines') AS status;
-- PC - CMM (41) → machinetypeid=33, pctypeid=5 (CMM)
UPDATE machines
SET machinetypeid = 33, pctypeid = 5
WHERE machinetypeid = 41;
SELECT CONCAT('Migrated ', ROW_COUNT(), ' PC-CMM machines') AS status;
-- Handle any other PC types that might exist in production
-- PC - Shopfloor (37) → machinetypeid=33, pctypeid=3 (Shopfloor)
UPDATE machines
SET machinetypeid = 33, pctypeid = 3
WHERE machinetypeid = 37 AND pctypeid IS NULL;
-- PC - Engineer (38) → machinetypeid=33, pctypeid=2 (Engineer)
UPDATE machines
SET machinetypeid = 33, pctypeid = 2
WHERE machinetypeid = 38 AND pctypeid IS NULL;
-- PC - Wax Trace (42) → machinetypeid=33, pctypeid=6 (Wax / Trace)
UPDATE machines
SET machinetypeid = 33, pctypeid = 6
WHERE machinetypeid = 42 AND pctypeid IS NULL;
-- Catch-all: Any remaining 34-46 → machinetypeid=33, pctypeid=4 (Uncategorized)
UPDATE machines
SET machinetypeid = 33, pctypeid = 4
WHERE machinetypeid BETWEEN 34 AND 46 AND pctypeid IS NULL;
SELECT CONCAT('Total machines now using machinetypeid 34-46: ',
(SELECT COUNT(*) FROM machines WHERE machinetypeid BETWEEN 34 AND 46)) AS status;
-- ============================================================================
-- 3. UPDATE MODELS TO USE GENERIC PC MACHINETYPE (33)
-- ============================================================================
UPDATE models
SET machinetypeid = 33
WHERE machinetypeid BETWEEN 34 AND 46;
SELECT CONCAT('Updated ', ROW_COUNT(), ' models to generic PC type') AS status;
-- ============================================================================
-- 4. DELETE REDUNDANT MACHINETYPES
-- ============================================================================
-- Keep 33 (PC) and 44 (USB Device), remove 34-43 and 45-46
DELETE FROM machinetypes WHERE machinetypeid BETWEEN 34 AND 43;
SELECT CONCAT('Deleted ', ROW_COUNT(), ' machinetypes (34-43)') AS status;
DELETE FROM machinetypes WHERE machinetypeid BETWEEN 45 AND 46;
SELECT CONCAT('Deleted ', ROW_COUNT(), ' machinetypes (45-46)') AS status;
-- ============================================================================
-- VERIFICATION
-- ============================================================================
SELECT 'VERIFICATION - Remaining machinetypes >= 33:' AS info;
SELECT machinetypeid, machinetype FROM machinetypes WHERE machinetypeid >= 33;
SELECT 'VERIFICATION - Machines by pctypeid:' AS info;
SELECT pt.typename, COUNT(*) as count
FROM machines m
JOIN pctype pt ON m.pctypeid = pt.pctypeid
WHERE m.pctypeid IS NOT NULL
GROUP BY m.pctypeid
ORDER BY count DESC;
-- ============================================================================
-- COMMIT (uncomment when ready to apply)
-- ============================================================================
COMMIT;
-- ROLLBACK; -- Use this instead if something looks wrong
SELECT 'Migration completed successfully!' AS status;