{
  "audit": {
    "title": "Vienotais Zemes Reģistrs Security Audit",
    "target": "https://vienotaiszemesregistrs.tech/hakatons/#/",
    "audit_date": "2026-10-26",
    "auditor_type": "automated_remote",
    "methodology": "External remote-only testing; curl, openssl s_client, dig, static JS analysis",
    "overall_risk_score": 8.2,
    "overall_risk_level": "HIGH"
  },
  "findings": [
    {
      "id": "F-001",
      "title": "Unrestricted CORS: wildcard access-control-allow-origin on all HTTP methods and paths",
      "category": "CORS",
      "severity": "HIGH",
      "score": 8.5,
      "status": "confirmed",
      "description": "The application returns Access-Control-Allow-Origin: * (wildcard) which permits any origin to make cross-origin requests including authentication cookies and headers. Supported methods include GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD for all paths.",
      "evidence": {
        "request_headers": {
          "Origin": "https://evil.example.com"
        },
        "response_headers": {
          "access-control-allow-origin": "*",
          "access-control-allow-methods": "GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD",
          "access-control-max-age": "86400",
          "vary": "Origin"
        },
        "http_status": 200,
        "test_endpoint": "/hakatons/api/health"
      },
      "impact": "Enables cross-site request forgery, unauthenticated enumeration when backend is deployed, and data exfiltration from government data APIs by any third-party website.",
      "remediation": "Restrict CORS to specific allowlisted origins (e.g. https://vienotaiszemesregistrs.tech only). Set allowed methods to at-most GET + HEAD for the SPA frontend. For authenticated endpoints, use SameSite cookies or token-based auth.",
      "references": {
        "cwe": ["CWE-942"],
        "owasp": "https://owasp.org/www-community/attacks/CORS_OriginHeader_False_Trust",
        "evidence_file": "/tmp/vzr_headers.txt"
      }
    },
    {
      "id": "F-002",
      "title": "Missing 10 standard web security headers (content-security-policy, strict-transport, x-frame-options, x-content-type-options, etc.) on functional responses",
      "category": "Security Headers",
      "severity": "HIGH",
      "score": 7.5,
      "status": "confirmed",
      "description": "None of the 10 standard web security headers were returned on success (HTTP 200) responses for /hakatons/ (SPA) or /api/health endpoints. This includes missing X-Frame-Options, X-Content-Type-Options, Content-Security-Policy, Strict-Transport-Security, and others.",
      "evidence": {
        "test_endpoint": "/hakatons/",
        "http_status": 200,
        "headers_checked": [
          {"name": "Strict-Transport-Security", "present": false},
          {"name": "X-Content-Type-Options", "present": false},
          {"name": "X-Frame-Options", "present": false},
          {"name": "Content-Security-Policy", "present": false},
          {"name": "Cross-Origin-Opener-Policy", "present": false},
          {"name": "Cross-Origin-Resource-Policy", "present": false},
          {"name": "Referrer-Policy", "present": false},
          {"name": "Permissions-Policy", "present": false},
          {"name": "X-XSS-Protection", "present": false},
          {"name": "Cache-Control", "present": true, "value": "public, max-age=0"}
        ],
        "raw_header_dump": "/tmp/vzr_headers.txt"
      },
      "impact": "No clickjacking protection, no MIME sniffing mitigation, no HTTP downgrade prevention, and no browser-caching restrictions on sensitive application responses.",
      "remediation": "In Caddy configuration: add all missing security headers globally. Recommended values: Strict-Transport-Security (max-age=31536000; includeSubDomains), X-Content-Type-Options (nosniff), X-Frame-Options (DENY), Content-Security-Policy (see F-010), Cross-Origin-Opener-Policy (same-origin), Referrer-Policy (strict-origin-when-cross-origin).",
      "references": {
        "cwe": ["CWE-693"],
        "owasp": "https://owasp.org/www-project-secure-headers/",
        "evidence_file": "/tmp/vzr_headers.txt"
      }
    },
    {
      "id": "F-003",
      "title": "No HTTP Strict-Transport-Security (HSTS) enforcement on any response",
      "category": "Transport Security",
      "severity": "MEDIUM",
      "score": 6.5,
      "status": "candidate",
      "description": "Although the server correctly redirects HTTP requests to HTTPS via 308 status code, no Strict-Transport-Security header is present on any response. This means a browser making its first connection has no instruction to always use HTTPS.",
      "evidence": {
        "http_redirect_test": {"method": "GET", "url": "http://vienotaiszemesregistrs.tech/hakatons/", "status": 308, "location": "https://vienotaiszemesregistrs.tech/hakatons/"},
        "hsts_header_check": "absent (grep of Strict-Transport-Security returned null)"
      },
      "impact": "First-time visitors who type http:// are vulnerable to SSL stripping attacks by man-in-the-middle before their first HTTPS connection.",
      "remediation": "Add Strict-Transport-Security header with max-age=31536000 on the production Caddy config. Add domain to HSTS preload list for additional protection.",
      "references": {
        "cwe": ["CWE-319"],
        "owasp": "https://owasp.org/www-project-secure-headers/#strict-transport-security",
        "evidence_file": "/tmp/vzr_headers.txt"
      }
    },
    {
      "id": "F-004",
      "title": "DNS security posture weak: no DNSSEC, no CAA, no TXT/SPF/DKIM/DMARC records, no IPv6 support",
      "category": "DNS Security",
      "severity": "MEDIUM",
      "score": 6.0,
      "status": "confirmed",
      "description": "The domain vienotaiszemesregistrs.tech has virtually no DNS security infrastructure: no DNSSEC validation chain (zone susceptible to cache poisoning), no CAA record (any certificate authority can issue certificates), no TXT records for email authentication, no MX routing, and no AAAA IPv6 address.",
      "evidence": {
        "dnssec_results": "no RRSIG records found",
        "caa_record": "none - any CA can issue certificates",
        "spf_record": "none (TXT record empty)",
        "dkim_record": "none configured",
        "dmarc_record": "none configured",
        "mx_records": "none returned by dig MX query",
        "aaaa_record": "none - no IPv6 connectivity",
        "a_record": "158.247.224.82"
      },
      "impact": "Certificate spoofing by unauthorized CAs, email forgery from the domain (no SPF/DMARC), DNS response poisoning (no DNSSEC), incomplete network reachability (no IPv6 AAAA).",
      "remediation": "1) Enable DNSSEC in registrar. 2) Add CAA record: CAA 0 issue \"letsencrypt.org\" (or current CA). 3) Add TXT SPF record: TXT \"v=spf1 -all\" if no email expected. 4) Add DMARC TXT record for policy enforcement.",
      "references": {
        "cwe": ["CWE-798", "CWE-296"],
        "owasp": "https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/02-Conduct_Information_Gathering/04-Enumerate_DNS_Name_Servers/"
      }
    },
    {
      "id": "F-005",
      "title": "/api/health endpoint leaks backend connectivity and operational metadata to unauthenticated origins via wildcard CORS",
      "category": "Information Disclosure",
      "severity": "MEDIUM",
      "score": 5.5,
      "status": "confirmed",
      "description": "The public-health check endpoint returns JSON with operational metadata (ok: true, ms latency, checked_at timestamp). Combined with F-001's CORS wildcard, any external origin can query this endpoint to perform reconnaissance on backend availability.",
      "evidence": {
        "endpoint_response": {"ok": true, "ms": 3, "checked_at": "2026-10-26T10:17:59.182Z"},
        "cors_header": "access-control-allow-origin: *",
        "http_status": 200,
        "test_endpoint": "/hakatons/api/health"
      },
      "impact": "Enables timing analysis for DDoS detection and rate-limit evasion; confirms API operational status to any observer; cross-origin accessible enabling reconnaissance from attacker-controlled websites.",
      "remediation": "Remove or suppress ok/ms timestamp fields from the response. Restrict access to this endpoint via IP allowlisting or token-based authentication for internal monitoring only.",
      "references": {
        "cwe": ["CWE-200"],
        "owasp": "https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/08-Testing_for_Error_Code_Handler/01-Testing_for_Stack_Traces"
      }
    },
    {
      "id": "F-006",
      "title": "Server framework fingerprinting through x-powered-by: Express header on all responses",
      "category": "Information Disclosure",
      "severity": "LOW",
      "score": 4.0,
      "status": "confirmed",
      "description": "The X-Powered-By: Express HTTP response header is present on all API responses and SPA responses, identifying the backend as a Node.js/Express application. This information helps attackers understand the technology stack.",
      "evidence": {
        "header_line": "x-powered-by: Express",
        "present_on_api": true,
        "present_on_spa": true,
        "test_endpoint": "/hakatons/api/health"
      },
      "impact": "Information disclosure revealing backend framework type. Attackers use this to tailor exploits for known Express/framework vulnerabilities and narrow their attack surface knowledge.",
      "remediation": "Add app.disable('x-powered-by') early in Express application initialization, or remove the express middleware that sets this header. In Caddy, strip it with: header -X-Powered-By",
      "references": {
        "cwe": ["CWE-200"],
        "owasp": "https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/01-Information_Gathering/"
      }
    },
    {
      "id": "F-007",
      "title": "Express default error pages leaked in unhandled routes contain path and HTTP method info (Cannot GET /api/info, Cannot GET /api/facts)",
      "category": "Information Disclosure",
      "severity": "LOW",
      "score": 4.5,
      "status": "confirmed",
      "description": "When API endpoints are not available (currently all return 404), Express returns its default error page containing literal \"Cannot GET /path\" text which confirms backend routing conventions and exposes the API path structure to attackers.",
      "evidence": {
        "error_responses": [
          {"endpoint": "/hakatons/api/info", "partial_body": "\"<p>Cannot GET /hakatons/api/info</p>\"", "http_status": 404},
          {"endpoint": "/hakatons/api/facts", "partial_body": "\"<p>Cannot GET /hakatons/api/facts</p>\"", "http_status": 404}
        ],
        "error_page_header": "x-powered-by: Express (still present on error pages)"
      },
      "impact": "Information disclosure revealing backend routing conventions and confirming which paths exist in the application. Combined with F-011, this tells attackers exactly which API endpoints were planned.",
      "remediation": "Replace default Express error handler with a custom one that returns generic JSON: {error: 'Internal server error'} without path/method details. Configure Caddy to serve custom error pages before SPA catch-all.",
      "references": {
        "cwe": ["CWE-209"],
        "owasp": "https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/08-Testing_for_Error_Code_Handler/"
      }
    },
    {
      "id": "F-008",
      "title": "No robots.txt — all application paths indexable by crawlers and search engines",
      "category": "Information Disclosure",
      "severity": "INFO",
      "score": 2.5,
      "status": "confirmed",
      "description": "The application does not serve a /robots.txt file. Requests to /hakatons/robots.txt receive a 308 redirect to the SPA main page instead of actual robots directives.",
      "evidence": {
        "query": "GET https://vienotaiszemesregistrs.tech/hakatons/robots.txt",
        "http_status": 308,
        "redirect_location": "https://vierotaiszemesregistrs.tech/hakatons/"
      },
      "impact": "Low risk — crawlers cannot meaningfully index API paths and the path structure itself is discoverable from JS bundle analysis (F-011).",
      "remediation": "Serve /hakatons/robots.txt with: User-agent: * Allow: / Disallow: /api/",
      "references": {
        "cwe": ["CWE-200"],
        "owasp": "https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/02-Conduct_Information_Gathering/"
      }
    },
    {
      "id": "F-009",
      "title": "No .well-known/security.txt for security vulnerability disclosure or responsible contact policy",
      "category": "Policy / Process",
      "severity": "INFO",
      "score": 2.0,
      "status": "candidate",
      "description": "No RFC 9116-compliant .well-known/security.txt file exists at the domain root, meaning legitimate security researchers have no standardized way to report discovered vulnerabilities.",
      "evidence": {
        "query": "GET https://vienotaiszemesregistrs.tech/.well-known/security.txt",
        "http_status": 404
      },
      "impact": "No direct technical vulnerability. Violates responsible disclosure best practices and could delay coordinated vulnerability remediation if vulnerabilities are discovered by external parties.",
      "remediation": "Deploy /.well-known/security.txt with contact email, canonical URL, policy URL, and preferred languages (lv, en).",
      "references": {
        "cwe": ["CWE-697"],
        "owasp": "https://owasp.org/www-community/vulnerabilities/Missing_Response_to_Report_Vulnerability"
      }
    },
    {
      "id": "F-010",
      "title": "No Content-Security-Policy (CSP) on success responses — CSP present only on error pages with default-src 'none' which would break application",
      "category": "Content Security Policy",
      "severity": "MEDIUM",
      "score": 6.0,
      "status": "candidate",
      "description": "HTML responses for the main SPA (/hakatons/) do not include a Content-Security-Policy header. A CSP value of default-src 'none' is set on error (HTTP 500) pages only — which would prevent all application scripts from loading if applied to the success page.",
      "evidence": {
        "spa_page_check": {"endpoint": "/hakatons/", "status": 200, "content_security_policy": "absent"},
        "error_page_check": {"http_status": 500, "content_security_policy": "default-src 'none'", "note": "CSP present on error pages but would break application if applied to SPA"}
      },
      "impact": "No browser-enforced script execution policy protects against XSS, unauthorized third-party resource loading, or data exfiltration. Combined with F-001's open CORS, this is a compounding vulnerability.",
      "remediation": "Deploy CSP for SPA with nonce-based script-src: script-src 'nonce-{server-generated}' 'strict-dynamic'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; default-src 'self'",
      "references": {
        "cwe": ["CWE-693"],
        "owasp": "https://owasp.org/www-community/controls/Content_Security_Policy"
      }
    },
    {
      "id": "F-011",
      "title": "SPA JavaScript bundle hardcodes calls to 14+ external government services (SSRF risk if served via server-side proxy)",
      "category": "SSRF / Insecure Direct Object Reference",
      "severity": "MEDIUM",
      "score": 5.0,
      "status": "candidate",
      "description": "Static analysis of the SPA bundle reveals hardcoded URLs for external data sources including Cadastre survey services (ozols.gov.lv), ArcGIS REST APIs (tapis.gov.lv), land parcel databases (zemesgramata.lv), cultural heritage objects (mantojums.lv), and government commercial registries.",
      "evidence": {
        "bundle_analysis": {
          "file": "/tmp/vzr_app.js",
          "size_bytes": 343852,
          "urls_by_category": {
            "express_server_api_routes": [
              {"url": "https://www.data.gov.lv/pakalpojumi/ur-reistrs/", "category": "UR registrs data"},
              {"url": "https://ozols.gov.lv/rest/services", "category": "Cadastre survey (ArcGIS REST)"},
              {"url": "https://tapis.gov.lv/GeoServer/wms", "category": "Construction info (WMS)"}
            ],
            "direct_to_external_hardcoded_urls": [
              {"url": "geolatvija.lv/geo/query", "category": "Geographic queries"},
              {"url": "geolatvija.lv/main/viewer", "category": "Map visualization"},
              {"url": "mantojums.lv/cultural-objects", "category": "Heritage objects"},
              {"url": "tapis.gov.lv/bisp/lv/Sync/FindConstructs", "category": "Planned constructions"},
              {"url": "zemesgramata.lv/lv/Search/*", "category": "Land parcel data"},
              {"url": "server.arcgisonline.com/ArcGIS/rest/", "type": "Imagery tiles"},
              {"url": "tile.openstreetmap.org/{z}/{x}/{y}", "type": "Map tiles"},
              {"url": "ekii.lv/...", "category": "Environmental info (partial)"},
              {"url": "liaa.business.gov.lv/...", "category": "Rural development data (partial)"},
              {"url": "cfla.gov.lv/...", "category": "Consumer protection (partial)"},
              {"url": "www.eis.gov.lv/...", "category": "EIS portal (partial)"},
              {"url": "lursoft.lv/...", "category": "Commercial registry (partial)"}
            ]
          }
        },
        "note": "SSRF risk applies if any Express server-side proxy is used for these calls"
      },
      "impact": "If backend proxy pattern is used, SSRF could redirect requests to internal services. Multiple data sources increase attack surface for data leakage and exfiltration.",
      "remediation": "1) Implement URL validation on all server-side fetches against a domain/IP allowlist. 2) Block responses from RFC 1918 private IP ranges. 3) Validate redirect chains for localhost/internal network targets. 4) Use explicit host:version verification for each allowed API.",
      "references": {
        "cwe": ["CWE-918"],
        "owasp": "https://owasp.org/www-community/vulnerabilities/Server_Side_Forge_Request",
        "evidence_file": "/tmp/vzr_app.js"
      }
    },
    {
      "id": "F-012",
      "title": "No Content-Security-Policy nonce or hash on type='module' scripts in SPA HTML response",
      "category": "Content Security Policy / Script Security",
      "severity": "MEDIUM",
      "score": 5.5,
      "status": "candidate",
      "description": "The SPA HTML references its application JavaScript via <script type='module'> without a nonce attribute or integrity hash. When CSP is deployed (F-010), this will cause all module scripts to be blocked unless a server-generated nonce is dynamically embedded in the response HTML.",
      "evidence": {
        "spa_html_snippet_from_body": "<script type=\"module\" crossorigin src=\"/hakatons/assets/index-BAyDyg0i.js\"></script>",
        "crossorigin_present": true,
        "nonce_present": false,
        "integrity_hash_present": false,
        "evidence_file": "/tmp/vzr_body.txt"
      },
      "impact": "Once CSP is configured for the SPA, all application functionality will break because module scripts cannot use 'unsafe-inline' and dynamic nonce injection is not configured.",
      "remediation": "Option A: Deploy server-side nonces dynamically in HTML via Caddy headers/transform middleware. Option B: Add integrity hash using Subresource Integrity (SRI): integrity=\"sha256-{sha256-of-bundle}\" on the script tag.",
      "references": {
        "cwe": ["CWE-79"],
        "owasp": "https://owasp.org/www-community/attacks/xss/",
        "evidence_file": "/tmp/vzr_body.txt"
      }
    }
  ]
}
