# Classic ASP Development Standards
## ShopDB Application
**Version:** 1.2
**Last Updated:** 2025-12-12
**Status:** MANDATORY for all new development and modifications
---
## Table of Contents
1. [Security Standards](#security-standards)
2. [Database Access Standards](#database-access-standards)
3. [Input Validation Standards](#input-validation-standards)
4. [Output Encoding Standards](#output-encoding-standards)
5. [Error Handling Standards](#error-handling-standards)
6. [Code Structure Standards](#code-structure-standards)
7. [Naming Conventions](#naming-conventions)
8. [Documentation Standards](#documentation-standards)
9. [Performance Standards](#performance-standards)
10. [Testing Standards](#testing-standards)
---
## Security Standards
### Authentication & Authorization
> **NOTE:** Authentication is not yet implemented. SAML integration is planned for a future release. The patterns below document the intended implementation.
**MANDATORY (when implemented):** All pages MUST implement authentication checks.
```vbscript
<%
' This will redirect to login if user is not authenticated
Call RequireAuthentication()
' For administrative functions:
Call RequireRole("Admin")
%>
```
**Exception:** Only the following pages may skip authentication:
- `login.asp`
- `error.asp`
- `404.asp`
- API endpoints (use API key authentication instead)
### Session Management
```vbscript
' Standard session configuration (in sql.asp)
Session.Timeout = APP_SESSION_TIMEOUT ' From config.asp
' After successful authentication:
Session("authenticated") = True
Session("userId") = userId
Session("userName") = userName
Session("userRole") = userRole
Session("loginTime") = Now()
Session.Abandon ' Only on explicit logout
```
### Password Requirements
- **Minimum Length:** 12 characters
- **Complexity:** Must include uppercase, lowercase, number, special character
- **Storage:** Never store plaintext passwords
- **Transmission:** HTTPS only (enforce in IIS)
### Security Headers
All pages MUST set appropriate security headers:
```vbscript
Response.AddHeader "X-Content-Type-Options", "nosniff"
Response.AddHeader "X-Frame-Options", "SAMEORIGIN"
Response.AddHeader "X-XSS-Protection", "1; mode=block"
Response.AddHeader "Content-Security-Policy", "default-src 'self'"
```
---
## Database Access Standards
### Connection String
**MANDATORY:** Use configuration file, NEVER hard-code credentials.
```vbscript
<%
' In sql.asp - use config constants
objConn.ConnectionString = GetConnectionString()
objConn.Open
%>
```
**Environment Toggle:** Set `USE_DSN` in config.asp:
- `USE_DSN = False` - Development (direct ODBC driver connection)
- `USE_DSN = True` - Production (DSN-based connection)
```vbscript
' config.asp controls connection method
Const USE_DSN = False ' Set True for production
Function GetConnectionString()
If USE_DSN Then
' Production: DSN-based
GetConnectionString = "DSN=" & DB_DSN & ";Uid=...;Pwd=...;Option=3;Pooling=True;Max Pool Size=100;"
Else
' Development: Direct ODBC
GetConnectionString = "Driver={" & DB_DRIVER & "};Server=...;..."
End If
End Function
```
### Parameterized Queries
**MANDATORY:** ALL database queries MUST use parameterization.
** NEVER DO THIS:**
```vbscript
' WRONG - SQL Injection vulnerable
machineId = Request.QueryString("machineid")
strSQL = "SELECT * FROM machines WHERE machineid = " & machineId
Set rs = objConn.Execute(strSQL)
```
** ALWAYS DO THIS:**
```vbscript
' CORRECT - Parameterized query
machineId = GetSafeInteger("QS", "machineid", 0, 1, 999999)
Set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = objConn
cmd.CommandText = "SELECT * FROM machines WHERE machineid = ?"
cmd.CommandType = 1 ' adCmdText
Set param = cmd.CreateParameter("@machineid", 3, 1, , machineId) ' 3=adInteger, 1=adParamInput
cmd.Parameters.Append param
Set rs = cmd.Execute()
```
### Resource Cleanup
**MANDATORY:** Always clean up database resources.
```vbscript
<%
' At the end of EVERY page:
Call CleanupResources()
%>
```
**Template:**
```vbscript
<%
On Error Resume Next
' Database operations here
' Before any Response.Redirect:
Call CleanupResources()
Response.Redirect("page.asp")
Response.End
' At end of page:
Call CleanupResources()
On Error Goto 0
%>
```
### Connection Pooling
**MANDATORY:** Enable connection pooling in configuration.
```vbscript
' In config.asp GetConnectionString() function:
connectionString = connectionString & "Pooling=True;Max Pool Size=100;"
```
---
## Input Validation Standards
### Validation Library
**MANDATORY:** Use validation functions for ALL user input.
```vbscript
```
### Common Validation Patterns
#### Integer IDs
```vbscript
Dim machineId
machineId = GetSafeInteger("QS", "machineid", 0, 1, 999999)
If machineId = 0 Then
Response.Redirect("error.asp?msg=INVALID_ID")
Response.End
End If
```
#### String Fields
```vbscript
Dim serialNumber
serialNumber = GetSafeString("FORM", "serialnumber", "", 7, 50, "^[A-Z0-9]+$")
If serialNumber = "" Then
Response.Redirect("adddevice.asp?error=INVALID_SERIAL")
Response.End
End If
```
#### IP Addresses
```vbscript
Dim ipAddress
ipAddress = Request.Form("ipaddress")
If Not ValidateIPAddress(ipAddress) Then
Response.Redirect("error.asp?msg=INVALID_IP")
Response.End
End If
```
#### Email Addresses
```vbscript
Dim email
email = Request.Form("email")
If Not ValidateEmail(email) Then
Response.Redirect("error.asp?msg=INVALID_EMAIL")
Response.End
End If
```
### Whitelist Validation
**PREFERRED:** Use whitelist validation whenever possible.
```vbscript
' Example: Only allow specific status values
Dim status
status = Request.Form("status")
If status <> "active" And status <> "inactive" And status <> "pending" Then
Response.Redirect("error.asp?msg=INVALID_STATUS")
Response.End
End If
```
### Client-Side Validation
**REQUIRED:** Implement client-side validation for user experience.
**CRITICAL:** Client-side validation does NOT replace server-side validation.
```html
```
---
## Output Encoding Standards
### HTML Output
**MANDATORY:** ALL user-controlled output MUST be HTML-encoded.
** NEVER DO THIS:**
```vbscript
<%=rs("machinename")%>
<%Response.Write(rs("description"))%>
```
** ALWAYS DO THIS:**
```vbscript
<%=Server.HTMLEncode(rs("machinename"))%>
<%Response.Write(Server.HTMLEncode(rs("description")))%>
```
### JavaScript Context
**MANDATORY:** Use JavaScript encoding for data in JavaScript.
```vbscript
```
```vbscript
' Helper function in includes/encoding.asp
Function JavaScriptEncode(str)
Dim result
result = Replace(str, "\", "\\")
result = Replace(result, "'", "\'")
result = Replace(result, """", "\""")
result = Replace(result, vbCrLf, "\n")
result = Replace(result, vbCr, "\n")
result = Replace(result, vbLf, "\n")
JavaScriptEncode = result
End Function
```
### URL Parameters
**MANDATORY:** Use URLEncode for URL parameters.
```vbscript
">Link
```
### JSON Output
**MANDATORY:** Properly escape JSON output.
```vbscript
<%
Response.ContentType = "application/json"
Response.Write(CreateJSONFromRecordset(rs))
%>
```
---
## Error Handling Standards
### Standard Error Handler
**MANDATORY:** Include error handler in ALL pages.
```vbscript
<%
Call InitializeErrorHandling("pagename.asp")
' Page logic here
Call CheckForErrors() ' After each critical operation
Call CleanupResources()
%>
```
### Error Logging
**MANDATORY:** Log all errors to server-side log file.
```vbscript
' In error_handler.asp
Call LogError(pageName, Err.Number, Err.Description, Request.ServerVariables("REMOTE_ADDR"))
```
**Log Format:**
```
2025-10-10 14:35:22 | displaymachine.asp | -2147467259 | Syntax error in SQL | 192.168.122.1
```
### User-Facing Error Messages
**MANDATORY:** NEVER expose technical details to users.
** WRONG:**
```vbscript
Response.Write("Error: " & Err.Description)
```
** CORRECT:**
```vbscript
Response.Redirect("error.asp?code=DATABASE_ERROR")
```
### Error Codes
Standard error codes for user messaging:
- `INVALID_INPUT` - User input validation failed
- `NOT_FOUND` - Record not found
- `UNAUTHORIZED` - User lacks permission
- `DATABASE_ERROR` - Database operation failed
- `GENERAL_ERROR` - Catch-all for unexpected errors
---
## Code Structure Standards
### File Header
**MANDATORY:** Every ASP file must have a header comment block.
```vbscript
<%
'=============================================================================
' FILE: displaymachine.asp
' PURPOSE: Display detailed information for a single machine
'
' PARAMETERS:
' machineid (QueryString, Required) - Integer ID of machine to display
'
' DEPENDENCIES:
' - includes/config.asp - Application configuration
' - includes/sql.asp - Database connection
' - includes/validation.asp - Input validation functions
' - includes/auth_check.asp - Authentication verification
'
' DATABASE TABLES:
' - machines (primary)
' - machinetypes, models, vendors, businessunits
' - printers (LEFT JOIN - may be NULL)
' - communications (LEFT JOIN - may be NULL)
'
' SECURITY:
' - Requires authentication
' - No special role required (read-only)
' - Uses parameterized queries
'
' AUTHOR: [Your Name]
' CREATED: 2025-10-10
' MODIFIED: 2025-10-10 - Initial version
'
'=============================================================================
%>
```
### Standard Page Template
```vbscript
<%@ Language=VBScript %>
<%
Option Explicit ' MANDATORY - Forces variable declaration
%>
Page Title
<%
'-----------------------------------------------------------------------------
' AUTHENTICATION
'-----------------------------------------------------------------------------
Call RequireAuthentication()
'-----------------------------------------------------------------------------
' INITIALIZATION
'-----------------------------------------------------------------------------
Call InitializeErrorHandling("pagename.asp")
' Get and validate parameters
Dim paramId
paramId = GetSafeInteger("QS", "id", 0, 1, 999999)
If paramId = 0 Then
Call CleanupResources()
Response.Redirect("error.asp?msg=INVALID_ID")
Response.End
End If
' Get theme preference
Dim theme
theme = Request.Cookies("theme")
If theme = "" Then theme = "bg-theme1"
'-----------------------------------------------------------------------------
' DATABASE OPERATIONS
'-----------------------------------------------------------------------------
Dim strSQL, objRS
strSQL = "SELECT * FROM tablename WHERE id = ?"
Set objRS = ExecuteParameterizedQuery(objConn, strSQL, Array(paramId))
Call CheckForErrors()
If objRS.EOF Then
Call CleanupResources()
Response.Redirect("error.asp?msg=NOT_FOUND")
Response.End
End If
%>
<%=Server.HTMLEncode(objRS("name"))%>
<%
'-----------------------------------------------------------------------------
' CLEANUP
'-----------------------------------------------------------------------------
Call CleanupResources()
%>
```
### Form Processing Template
```vbscript
<%@ Language=VBScript %>
<%
Option Explicit
%>
<%
'-----------------------------------------------------------------------------
' AUTHENTICATION
'-----------------------------------------------------------------------------
Call RequireAuthentication()
Call RequireRole("Editor") ' If write operation requires special role
'-----------------------------------------------------------------------------
' INITIALIZATION
'-----------------------------------------------------------------------------
Call InitializeErrorHandling("savepage.asp")
'-----------------------------------------------------------------------------
' VALIDATE INPUT
'-----------------------------------------------------------------------------
Dim recordId, fieldValue1, fieldValue2
recordId = GetSafeInteger("FORM", "id", 0, 0, 999999)
fieldValue1 = GetSafeString("FORM", "field1", "", 1, 100, "^[A-Za-z0-9 ]+$")
fieldValue2 = GetSafeString("FORM", "field2", "", 0, 200, "")
If fieldValue1 = "" Then
Call CleanupResources()
Response.Redirect("editpage.asp?id=" & recordId & "&error=REQUIRED_FIELD")
Response.End
End If
'-----------------------------------------------------------------------------
' DATABASE OPERATION
'-----------------------------------------------------------------------------
Dim strSQL
If recordId > 0 Then
' Update existing record
strSQL = "UPDATE tablename SET field1 = ?, field2 = ?, lastupdated = NOW() WHERE id = ?"
Call ExecuteParameterizedUpdate(objConn, strSQL, Array(fieldValue1, fieldValue2, recordId))
Else
' Insert new record
strSQL = "INSERT INTO tablename (field1, field2, created) VALUES (?, ?, NOW())"
Call ExecuteParameterizedInsert(objConn, strSQL, Array(fieldValue1, fieldValue2))
recordId = CLng(objConn.Execute("SELECT LAST_INSERT_ID() AS id")(0))
End If
Call CheckForErrors()
'-----------------------------------------------------------------------------
' CLEANUP AND REDIRECT
'-----------------------------------------------------------------------------
Call CleanupResources()
Response.Redirect("displaypage.asp?id=" & recordId & "&success=1")
%>
```
---
## Naming Conventions
### Variables
**Style:** camelCase
```vbscript
' IDs - use "Id" suffix
Dim machineId, printerId, userId
' Strings - descriptive names
Dim serialNumber, ipAddress, userName, description
' Booleans - use "is" or "has" prefix
Dim isActive, hasPermission, isValid
' Database objects - use obj prefix
Dim objConn, objCmd, objRS
' SQL queries - use str prefix
Dim strSQL, strSQL2
' Counters/indexes - single letter or descriptive
Dim i, j, rowCount, itemIndex
```
### Constants
**Style:** UPPER_CASE_WITH_UNDERSCORES
```vbscript
Const DB_SERVER = "192.168.122.1"
Const MAX_FILE_SIZE = 10485760
Const SESSION_TIMEOUT = 30
Const DEFAULT_PAGE_SIZE = 50
```
### Functions
**Style:** PascalCase, verb-noun format
```vbscript
Function GetMachineById(machineId)
Function ValidateIPAddress(ipAddress)
Function RenderVendorDropdown(selectedId, filterType)
Function CreateJSONResponse(success, message, data)
Function CalculateTotalCost(items)
```
### Subroutines
**Style:** PascalCase, verb-noun format
```vbscript
Sub InitializeErrorHandling(pageName)
Sub CleanupResources()
Sub RequireAuthentication()
Sub LogError(source, errorNum, errorDesc)
```
### Files
**Display Pages (single record):** display[noun-singular].asp
- `displaymachine.asp`
- `displayprinter.asp`
- `displaypc.asp`
**List Pages (multiple records):** display[noun-plural].asp
- `displaymachines.asp`
- `displayprinters.asp`
- `displaypcs.asp`
**Edit Pages:** edit[noun-singular].asp
- `editmachine.asp`
- `editprinter.asp`
- `editpc.asp`
**Add Pages:** add[noun-singular].asp
- `addmachine.asp`
- `addprinter.asp`
- `addpc.asp`
**Form Processors:** [verb][noun].asp
- `savemachine.asp`
- `updatemachine.asp`
- `deletemachine.asp`
**Include Files:** descriptive lowercase
- `sql.asp`
- `config.asp`
- `validation.asp`
- `error_handler.asp`
- `auth_check.asp`
### Database Tables
**Style:** lowercase, plural nouns
```sql
machines -- Unified table: Equipment, PCs, Network Devices
printers
communications -- Network interfaces (IP/MAC)
machinetypes
vendors
models
```
### Database Columns
**Style:** lowercase, descriptive
```sql
machineid
machinenumber
serialnumber
ipaddress
isactive
createdate
lastupdated
```
### Column Naming Gotchas
**IMPORTANT:** Be aware of these non-obvious column names:
| Expected | Actual | Table |
|----------|--------|-------|
| `ipaddress` | `address` | communications |
| `gateway` | `defaultgateway` | communications |
| `communicationid` | `comid` | 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 = 33` (generic PC type)
> **Note:** Redundant PC machinetypes (34-46) were removed. All PCs now use machinetypeid=33 with pctypeid for categorization (Standard, Engineer, Shopfloor, CMM, etc.)
---
## Documentation Standards
### Inline Comments
**REQUIRED:** Comment complex logic and business rules.
```vbscript
'-----------------------------------------------------------------------------
' Search Logic:
' 1. Check if input matches machine number (exact) or alias (partial)
' 2. If starts with "csf" and length=5, search printer CSF names
' 3. If 7 alphanumeric chars, treat as PC serial number
' 4. If valid IP, find containing subnet
' 5. If 9 digits, treat as SSO employee number
' 6. If starts with ticket prefix, redirect to ServiceNow
' 7. Otherwise, full-text search knowledge base
'-----------------------------------------------------------------------------
```
### SQL Query Comments
**RECOMMENDED:** Document complex queries.
```vbscript
'-----------------------------------------------------------------------------
' QUERY: Get machine with all related data
'
' Retrieves:
' - Machine details (machines table)
' - Type and function account (for billing)
' - Model and vendor information
' - Business unit assignment
' - Associated printer (LEFT JOIN - may be NULL)
' - Associated PC (LEFT JOIN - may be NULL)
'
' LEFT JOINs used because not all machines have printers/PCs.
'-----------------------------------------------------------------------------
strSQL = "SELECT m.*, mt.machinetype, mdl.modelnumber, " & _
" v.vendor, bu.businessunit, " & _
" p.ipaddress AS printerip " & _
"FROM machines m " & _
"INNER JOIN machinetypes mt ON m.machinetypeid = mt.machinetypeid " & _
"LEFT JOIN printers p ON m.printerid = p.printerid " & _
"WHERE m.machineid = ?"
```
### Function Documentation
**MANDATORY:** Document all functions and subroutines.
```vbscript
'-----------------------------------------------------------------------------
' FUNCTION: ValidateIPAddress
' PURPOSE: Validates that a string is a valid IPv4 address
'
' PARAMETERS:
' ipAddress (String) - The IP address to validate
'
' RETURNS:
' Boolean - True if valid IPv4 address, False otherwise
'
' EXAMPLES:
' ValidateIPAddress("192.168.1.1") -> True
' ValidateIPAddress("192.168.1.256") -> False
' ValidateIPAddress("not an ip") -> False
'
' VALIDATION:
' - Must match pattern: XXX.XXX.XXX.XXX
' - Each octet must be 0-255
' - No leading zeros allowed
'-----------------------------------------------------------------------------
Function ValidateIPAddress(ipAddress)
Dim objRegEx, pattern
Set objRegEx = New RegExp
pattern = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
objRegEx.Pattern = pattern
ValidateIPAddress = objRegEx.Test(ipAddress)
End Function
```
### TODO Comments
**ENCOURAGED:** Use standardized TODO format.
```vbscript
' TODO: SECURITY - Add authentication check (HIGH PRIORITY)
' TODO: PERFORMANCE - Cache this query result
' TODO: VALIDATION - Add email format validation
' TODO: REFACTOR - Extract to reusable function
' TODO: BUG - Handle null value edge case
```
---
## Performance Standards
### Database Query Optimization
**REQUIRED:** Follow these query optimization practices.
#### Use Specific Columns
```vbscript
' BAD
strSQL = "SELECT * FROM machines"
' GOOD
strSQL = "SELECT machineid, machinenumber, machinetype FROM machines"
```
#### Use Appropriate JOINs
```vbscript
' Use INNER JOIN when relationship is required
' Use LEFT JOIN when relationship is optional
' Avoid RIGHT JOIN (use LEFT JOIN instead for clarity)
```
#### Limit Result Sets
```vbscript
' For list views, always implement paging
strSQL = "SELECT * FROM machines WHERE isactive = 1 " & _
"ORDER BY machinenumber " & _
"LIMIT " & pageSize & " OFFSET " & offset
```
### Caching Strategy
**REQUIRED:** Cache reference data at application scope.
```vbscript
' In global.asa Application_OnStart
Sub Application_OnStart
' Cache rarely-changing reference data
Call LoadVendorCache()
Call LoadModelCache()
Call LoadMachineTypeCache()
End Sub
' In data_cache.asp
Function GetCachedVendors()
If IsEmpty(Application("CachedVendors")) Or _
DateDiff("s", Application("VendorCacheTime"), Now()) > CACHE_DURATION Then
Call LoadVendorCache()
End If
GetCachedVendors = Application("CachedVendors")
End Function
```
**Cache Invalidation:**
- Time-based: 5-30 minutes for reference data
- Event-based: Invalidate when data is modified
- Manual: Provide admin function to clear cache
### Response Buffering
**REQUIRED:** Enable response buffering.
```vbscript
<%
Response.Buffer = True
%>
```
**Benefits:**
- Allows headers to be set after content generation
- Enables proper error handling with redirects
- Improves performance by sending larger chunks
### Minimize Database Roundtrips
**PREFERRED:** Consolidate queries when possible.
```vbscript
' BAD - 4 separate queries
Set rsVendors = objConn.Execute("SELECT * FROM vendors")
Set rsModels = objConn.Execute("SELECT * FROM models")
Set rsTypes = objConn.Execute("SELECT * FROM machinetypes")
Set rsUnits = objConn.Execute("SELECT * FROM businessunits")
' BETTER - Use cached data
Response.Write(RenderCachedVendorDropdown())
Response.Write(RenderCachedModelDropdown())
Response.Write(RenderCachedTypeDropdown())
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`.
### API Endpoint Testing
**REQUIRED:** Test API endpoints used by PowerShell scripts after changes to api.asp.
```bash
# Health check
curl -s "http://192.168.122.151:8080/api.asp?action=getDashboardData"
# Get shopfloor PCs
curl -s "http://192.168.122.151:8080/api.asp?action=getShopfloorPCs"
# Simulate PowerShell PC data collection
curl -s -X POST "http://192.168.122.151:8080/api.asp" \
-d "action=updateCompleteAsset" \
-d "hostname=TESTPC01" \
-d "serialNumber=TEST123" \
-d "manufacturer=Dell Inc." \
-d "model=OptiPlex 7080" \
-d "osVersion=Microsoft Windows 11 Pro" \
-d "pcType=Standard"
# Get recorded IP (POST method)
curl -s -X POST "http://192.168.122.151:8080/api.asp" \
-d "action=getRecordedIP" \
-d "hostname=TESTPC01"
```
**Key API Endpoints:**
| Action | Method | Purpose |
|--------|--------|---------|
| getDashboardData | GET | Health check |
| getShopfloorPCs | GET | List shopfloor PCs |
| updateCompleteAsset | POST | PC data collection (main PowerShell endpoint) |
| getRecordedIP | POST | Get recorded IP for hostname |
| updatePrinterMapping | POST | Link printer to PC |
| updateInstalledApps | POST | Record installed applications |
### Unit Testing
**REQUIRED:** Test all validation functions.
Create test file: `tests/test_validation.asp`
```vbscript
<%
Sub TestValidateIPAddress()
If ValidateIPAddress("192.168.1.1") Then
Response.Write("PASS: Valid IP accepted
")
Else
Response.Write("FAIL: Valid IP rejected
")
End If
If Not ValidateIPAddress("999.999.999.999") Then
Response.Write("PASS: Invalid IP rejected
")
Else
Response.Write("FAIL: Invalid IP accepted
")
End If
End Sub
Call TestValidateIPAddress()
%>
```
### Integration Testing
**RECOMMENDED:** Test critical user flows.
**Test Cases:**
1. User login flow
2. Machine creation flow
3. Machine update flow
4. Search functionality
5. Report generation
### Security Testing
**REQUIRED:** Test for common vulnerabilities.
**Test Checklist:**
- [ ] SQL injection attempts on all input fields
- [ ] XSS payloads in all text fields
- [ ] Access control bypass attempts
- [ ] Session hijacking scenarios
- [ ] CSRF token validation
### Load Testing
**RECOMMENDED:** Test under expected load.
**Metrics to Monitor:**
- Response time per page
- Database connection pool usage
- Memory consumption
- Concurrent user capacity
---
## Code Review Checklist
Before committing code, verify:
### Security
- [ ] Authentication check present
- [ ] All queries use parameterization
- [ ] All output is HTML-encoded
- [ ] Input validation implemented
- [ ] No credentials in code
### Error Handling
- [ ] Error handler included
- [ ] Resources cleaned up on all paths
- [ ] No technical details exposed to users
- [ ] Errors logged to server
### Code Quality
- [ ] File header present
- [ ] Complex logic commented
- [ ] Naming conventions followed
- [ ] No code duplication
- [ ] No commented-out debug code
### Performance
- [ ] Queries optimized
- [ ] Appropriate caching used
- [ ] Resources properly closed
- [ ] Result sets limited/paged
### Testing
- [ ] Manually tested happy path
- [ ] Tested error conditions
- [ ] Tested with invalid input
- [ ] Cross-browser tested (if UI changes)
---
## Configuration Management
### Environment-Specific Configurations
**Structure:**
```
/includes/
config.asp.example (Template with placeholder credentials - tracked in git)
config.asp (Actual credentials - gitignored, never commit)
```
**Deployment Process:**
1. Copy `config.asp.example` to `config.asp`
2. Fill in actual credentials
3. Set `USE_DSN = True` for production, `False` for development
4. `config.asp` is gitignored - never committed to source control
**Configuration includes:**
- ShopDB credentials (with DSN/direct ODBC toggle)
- Employee database credentials (with DSN/direct ODBC toggle)
- Zabbix API URL and token
- Application settings (session timeout, page size, cache duration)
- Business logic constants (serial number length, CSF prefix, etc.)
### 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
See `includes/config.asp.example` for the full template. Key sections:
```vbscript
<%
'=============================================================================
' Database Configuration - ShopDB
'=============================================================================
' Toggle between DSN (production) and direct ODBC (development)
Const USE_DSN = False ' Set True for production
' DSN configuration (production)
Const DB_DSN = "shopdb"
Const DB_DSN_USER = "YOUR_DB_USER"
Const DB_DSN_PASSWORD = "YOUR_DB_PASSWORD"
' Direct ODBC configuration (development)
Const DB_DRIVER = "MySQL ODBC 9.4 Unicode Driver"
Const DB_SERVER = "192.168.122.1"
Const DB_PORT = "3306"
Const DB_NAME = "shopdb"
Const DB_USER = "YOUR_DB_USER"
Const DB_PASSWORD = "YOUR_DB_PASSWORD"
'=============================================================================
' Database Configuration - Employee Database
'=============================================================================
Const USE_EMP_DSN = True ' Usually DSN-based
Const EMP_DB_DSN = "wjf_employees"
Const EMP_DB_DSN_USER = "YOUR_EMP_USER"
Const EMP_DB_DSN_PASSWORD = "YOUR_EMP_PASSWORD"
'=============================================================================
' External Services
'=============================================================================
Const ZABBIX_URL = "http://your-zabbix-server/api_jsonrpc.php"
Const ZABBIX_API_TOKEN = "YOUR_ZABBIX_API_TOKEN"
'=============================================================================
' Helper Functions
'=============================================================================
Function GetConnectionString()
If USE_DSN Then
GetConnectionString = "DSN=" & DB_DSN & ";Uid=" & DB_DSN_USER & _
";Pwd=" & DB_DSN_PASSWORD & ";Option=3;Pooling=True;Max Pool Size=100;"
Else
GetConnectionString = "Driver={" & DB_DRIVER & "};Server=" & DB_SERVER & _
";Port=" & DB_PORT & ";Database=" & DB_NAME & _
";User=" & DB_USER & ";Password=" & DB_PASSWORD & _
";Option=3;Pooling=True;Max Pool Size=100;"
End If
End Function
%>
```
---
## Migration Guide
### Updating Existing Files to Meet Standards
**Priority Order:**
1. Add authentication check
2. Fix SQL injection vulnerabilities
3. Add HTML encoding to output
4. Add error handling
5. Add file header documentation
6. Refactor for code quality
### Example Migration
**Before (Non-Compliant):**
```vbscript
Machine
<%
machineid = Request.QueryString("machineid")
strSQL = "SELECT * FROM machines WHERE machineid = " & machineid
set rs = objconn.Execute(strSQL)
%>
<%=rs("machinename")%>
```
**After (Standards-Compliant):**
```vbscript
<%@ Language=VBScript %>
<%
Option Explicit
%>
Machine Details
<%
'-----------------------------------------------------------------------------
' FILE: displaymachine.asp
' PURPOSE: Display machine details
'-----------------------------------------------------------------------------
Call RequireAuthentication()
Call InitializeErrorHandling("displaymachine.asp")
Dim machineId, strSQL, objRS
machineId = GetSafeInteger("QS", "machineid", 0, 1, 999999)
If machineId = 0 Then
Call CleanupResources()
Response.Redirect("error.asp?msg=INVALID_ID")
Response.End
End If
strSQL = "SELECT * FROM machines WHERE machineid = ?"
Set objRS = ExecuteParameterizedQuery(objConn, strSQL, Array(machineId))
Call CheckForErrors()
If objRS.EOF Then
Call CleanupResources()
Response.Redirect("error.asp?msg=NOT_FOUND")
Response.End
End If
%>
<%=Server.HTMLEncode(objRS("machinename"))%>
<%
Call CleanupResources()
%>
```
---
## Enforcement
### Code Review Process
**MANDATORY:** All code changes must be reviewed before deployment.
**Reviewer Checklist:**
1. Standards compliance verified
2. Security vulnerabilities checked
3. Performance impact assessed
4. Documentation adequate
5. Tests passed
### Automated Checks
**RECOMMENDED:** Implement automated scanning where possible.
**Tools:**
- SQL injection scanner
- XSS vulnerability scanner
- Code style checker
- Dead code detector
### Training
**REQUIRED:** All developers must:
1. Read this standards document
2. Complete security training
3. Review example compliant code
4. Pass knowledge assessment
---
## Version History
| 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 |
| 1.2 | 2025-12-12 | Added DSN toggle documentation, API endpoint testing section, updated PC identification (machinetypeid=33 only), added comid column gotcha, noted auth not yet implemented, updated config file structure | Claude |
---
## Questions & Support
For questions about these standards:
1. Review the examples in this document
2. Check existing compliant code for patterns
3. Consult with team lead
4. Document unclear areas for future clarification
---
**REMEMBER:** These standards exist to protect our application and data. Following them is not optional—it's a requirement for all development work.