Extended POPIA compliance audit specifically for spatial data — checks for location-based PII, movement tracking, residential address inference.
Extended POPIA compliance audit specifically for spatial data — checks for location-based PII, movement tracking, residential address inference. Goes beyond the standard POPIA skill by addressing the unique privacy risks that arise from geographic and temporal location data.
Invoke when:
-- Check if spatial data can be joined to residential parcels with owner info
SELECT COUNT(*) FROM imported_data d
JOIN cadastral_parcels p ON ST_Within(d.geom, p.geom)
WHERE p.land_use IN ('RESIDENTIAL', 'RESIDENTIAL_MIXED');
Risk levels:
| Linkage Type | Risk | Action |
|---|---|---|
| Point in residential parcel | HIGH | Aggregate to suburb level for guests |
| Polygon overlapping residential | MEDIUM | Remove owner details in display |
| Line through residential area | LOW | No individual identification likely |
For aviation/drone data:
function assessFlightPrivacy(track: FlightTrack): RiskLevel {
if (track.callsign?.match(/^(SAA|FA|MN|KQ)/)) return 'LOW'; // Commercial
if (track.isPrivate && track.repeated > 3) return 'HIGH'; // Pattern
return 'MEDIUM';
}
Guest users must NEVER see:
function enforceGuestAggregation(features: Feature[], userRole: Role): Feature[] {
if (userRole === 'GUEST') {
return aggregateToSuburb(features); // Aggregate points to suburb polygons
}
return features;
}
Temporal + spatial data creates movement profiles:
Risk Matrix:
Spatial precision HIGH + Temporal precision HIGH = CRITICAL RISK
Spatial precision HIGH + Temporal precision LOW = HIGH RISK
Spatial precision LOW + Temporal precision HIGH = MEDIUM RISK
Spatial precision LOW + Temporal precision LOW = LOW RISK
Mitigations:
| Data Type | Retention | Justification |
|---|---|---|
| Real-time positions (OpenSky) | 30 days | Operational monitoring |
| Cached API responses | Per api_cache.expires_at | Performance |
| Historical tracks | 90 days | Planning analytics |
| Reconstruction imagery | 1 year | Asset lifecycle |
| Property valuation (GV Roll) | Until next GV Roll | Statutory |
-- Enforce retention via scheduled cleanup
DELETE FROM api_cache WHERE expires_at < NOW();
DELETE FROM flight_tracks WHERE created_at < NOW() - INTERVAL '90 days';
Required consent checks:
interface SpatialConsentChecklist {
dataContainsLocationPII: boolean; // Can location identify a person?
consentObtained: boolean; // Explicit opt-in?
consentScope: string; // What was consented to?
purposeLimitation: string; // Specific use case
rightToErasure: boolean; // Can we delete on request?
crossBorderTransfer: boolean; // Data leaving SA?
dataMinimisation: boolean; // Only necessary precision?
}
POPIA Spatial Audit Report:
═══════════════════════════════════════
POPIA SPATIAL AUDIT REPORT
Dataset: [name]
Date: [ISO 8601]
Auditor: [agent]
═══════════════════════════════════════
Risk Score: [LOW | MEDIUM | HIGH | CRITICAL]
☐ Residential parcel linkage: [PASS | FAIL | N/A]
☐ Individual tracking potential: [PASS | FAIL | N/A]
☐ Guest mode aggregation: [PASS | FAIL | N/A]
☐ Movement pattern inference: [PASS | FAIL | N/A]
☐ Retention policy compliance: [PASS | FAIL | N/A]
☐ Consent validation: [PASS | FAIL | N/A]
Required Annotations: [list of files needing POPIA blocks]
Recommended Actions: [specific mitigations]
═══════════════════════════════════════
public/mock/ with synthetic coordinates