Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
7 changes: 4 additions & 3 deletions docs/CODEMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,9 @@ Open any `.cs` / `.xaml` / tool. The top of the file answers: what is this, who
| `src/SiteCheck.Core/LawfulUse.cs` | Consent checkbox strings and CheckScope names shown in every shell. Criminal Code s. 342.1 / 342.2 framing lives in docs/lawful-use.md; this file is UI copy only. |
| `src/SiteCheck.Core/LoginSurface.cs` | Allowlisted GET of common public sign-in and admin paths. Brochure sites without /admin are Present (correctly absent), not Attention. |
| `src/SiteCheck.Core/Models.cs` | Shared types for one run: FindingState, Finding, FixLine, NextStep, EdgeProfile, StackHint, CheckReport, authorization record. |
| `src/SiteCheck.Core/PeopleSoftSurface.cs` | GET-only detection of public PeopleSoft portal paths (/psp/, /psc/, /ps/, /PSIGW/, /PSEMHUB/) plus PSJSESSIONID. Names CVE-2026-35273 as a patch prompt, not RCE proof. |
| `src/SiteCheck.Core/EnterpriseSurface.cs` | Table of other internet-facing enterprise portals. Attention cards only when advertised. CISA KEV CVE as patch prompt. |
| `src/SiteCheck.Core/AdvertisedMatch.cs` | False-positive guards: 2xx/3xx is not enough; Next.js brochure shells and trailing-slash redirects are not portals. Marketing copy that names PeopleSoft is ignored. |
| `src/SiteCheck.Core/PeopleSoftSurface.cs` | GET-only detection of public PeopleSoft portal paths (/psp/, /psc/, /ps/, /PSIGW/, /PSEMHUB/) plus PSJSESSIONID. Attention only with portal chrome or that cookie, not the word PeopleSoft on a brochure. Names CVE-2026-35273 as a patch prompt, not RCE proof. |
| `src/SiteCheck.Core/EnterpriseSurface.cs` | Table of other internet-facing enterprise portals. Attention cards only when advertised (runtime chrome, cookies, headers). CISA KEV CVE as patch prompt. |
| `src/SiteCheck.Core/WellKnownIdpSurface.cs` | GET-only OpenID/OAuth metadata. Attention only when JSON issuer is advertised. |
| `src/SiteCheck.Core/PrivateIp.cs` | Classify resolved addresses: RFC1918, loopback, link-local, CGNAT 100.64/10, NAT64, 6to4, unique-local, documentation ranges. |
| `src/SiteCheck.Core/ReportJson.cs` | Machine-readable sibling of ReportText for operators who archive JSON. |
Expand Down Expand Up @@ -109,7 +110,7 @@ Open any `.cs` / `.xaml` / tool. The top of the file answers: what is this, who
| `tests/SiteCheck.Tests/HostnameTests.cs` | Locks accept/reject table: public DNS names in, IPs/localhost/home suffixes/ports out. |
| `tests/SiteCheck.Tests/HtmlSurfaceTests.cs` | Locks mixed-content / SRI / tabnabbing / form-action observations on capped HTML. |
| `tests/SiteCheck.Tests/LoginSurfaceTests.cs` | Locks sign-in/admin allowlist and Present-when-absent for brochure sites. |
| `tests/SiteCheck.Tests/PeopleSoftSurfaceTests.cs` | Locks PeopleSoft path list and CVE-2026-35273 prompt-only wording (not RCE proof). |
| `tests/SiteCheck.Tests/PeopleSoftSurfaceTests.cs` | Locks PeopleSoft path list, brochure/Next-shell silence, and CVE-2026-35273 prompt-only wording (not RCE proof). |
| `tests/SiteCheck.Tests/EnterpriseSurfaceTests.cs` | Locks brochure silence, SharePoint/NetScaler/Magento prompts, and omitted exploit paths. |
| `tests/SiteCheck.Tests/PrivateIpTests.cs` | Locks RFC1918/CGNAT/ULA/etc. classification. |
| `tests/SiteCheck.Tests/ReportProtectTests.cs` | Locks DPAPI round-trip on Windows and no-op/skip elsewhere. |
Expand Down
2 changes: 1 addition & 1 deletion docs/cve-catalogue.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The catalogue covers products this program can actually see on a read-only publi

Plugin directory names on a WordPress homepage still do not get CVE matches. A folder name is not a version. This program does not download plugin files or `readme.html`. A GET of `/wp-admin/` is only a public-surface login-wall check.

Internet-facing **Oracle PeopleSoft** is a separate card. GET of common portal paths (`/psp/`, `/psc/`, `/PSIGW/`, `/PSEMHUB/`) plus homepage HTML and cookie names. If those paths answer, Rampart warns about **CVE-2026-35273** (PeopleTools 8.61 and 8.62, Oracle June 2026 CPU, CISA Known Exploited). That is a prompt to confirm the patch. It is not a proof of access. Rampart does not send the exploit or a WAF-bypass request. A brochure Next.js site that never names PeopleSoft stays Present.
Internet-facing **Oracle PeopleSoft** is a separate card. GET of common portal paths (`/psp/`, `/psc/`, `/PSIGW/`, `/PSEMHUB/`) plus the `PSJSESSIONID` cookie. Attention only when those responses show PeopleSoft portal chrome (for example `cmd=login` or `PORTAL_HOMEPAGE`) or that cookie. Copy on a brochure that names PeopleSoft is ignored, including this site. If the portal is advertised, Rampart warns about **CVE-2026-35273** (PeopleTools 8.61 and 8.62, Oracle June 2026 CPU, CISA Known Exploited). That is a prompt to confirm the patch. It is not a proof of access. Rampart does not send the exploit or a WAF-bypass request.

The same run also fingerprints other **internet-facing enterprise portals** from CISA's Known Exploited Vulnerabilities catalogue when the public surface advertises them (SharePoint ToolShell CVE-2025-53770 and CVE-2026-65660, NetScaler CVE-2026-88771, FortiGate CVE-2025-25249, BIG-IP APM CVE-2026-94127, Ivanti Connect Secure, GlobalProtect, Confluence, MOVEit, ScreenConnect, Artifactory, GitLab CVE-2026-85706, Adobe Commerce CVE-2026-71362, WebLogic, E-Business Suite, SAP NetWeaver, SonicWall SMA, vCenter, Zimbra). Those cards appear only when the product was advertised. They name the KEV CVE as a patch to confirm. They do not send exploit traffic. Paths used in past exploits (SharePoint ToolPane, WebLogic `wls-wsat`, FortiGate `fgt_lang` probes, BIG-IP `/mgmt/tm/util/bash`) are not requested.

Expand Down
2 changes: 1 addition & 1 deletion docs/what-is-checked.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ Plugin files and `readme.html` are not fetched. A GET of `/wp-admin/` is only a

**Certificate key size.** An RSA leaf shorter than 2048 bits is **Needs attention**.

**Oracle PeopleSoft.** GET `/psp/`, `/psc/`, `/ps/`, `/PSIGW/`, `/PSEMHUB/`. Homepage HTML and `PSJSESSIONID` are also read. If the hostname advertised PeopleSoft, the card is **Needs attention** and names CVE-2026-35273. Rampart does not send exploit traffic. Absence of those paths is **Present**.
**Oracle PeopleSoft.** GET `/psp/`, `/psc/`, `/ps/`, `/PSIGW/`, `/PSEMHUB/`. `PSJSESSIONID` is also read. The card is **Needs attention** only when those responses look like a PeopleSoft portal or that cookie is present. A page that merely writes the word PeopleSoft stays **Present**. CVE-2026-35273 is named as a patch prompt. Rampart does not send exploit traffic.

**Internet-facing enterprise portals.** The same GET batch also asks a short allowlist of distinctive public paths for on-premises products that CISA currently lists as known exploited when they are left on the internet: Microsoft SharePoint (`/_layouts/15/start.aspx`, never ToolPane), Citrix NetScaler Gateway, FortiGate SSL-VPN (`/remote/login` only), F5 BIG-IP TMUI/APM, Ivanti Connect Secure (`/dana-na/`), Palo Alto GlobalProtect, Confluence `/login.action`, Jira `/secure/Dashboard.jspa`, Microsoft Exchange `/owa/` and `/ecp/`, Jenkins (`X-Jenkins`), WSO2 Carbon, phpMyAdmin, Grafana, MOVEit Transfer, ScreenConnect SetupWizard, JFrog Artifactory, GitLab `/users/sign_in`, Adobe Commerce (homepage `X-Magento-Vary` / mage cookies), Oracle WebLogic `/console/` (never `/wls-wsat/`), Oracle E-Business Suite `OA_HTML`, SAP NetWeaver Web GUI, SonicWall SMA, VMware vSphere Client, and Zimbra. A card appears **only when that product was advertised**. Spring Boot `/actuator/env` and `/actuator/configprops` are treated as private files when they return non-HTML 200. A Next.js brochure that 404s those paths stays quiet. Each Attention card names the CISA KEV CVE as a **patch prompt**. It is not proof of RCE. A WAF rule is not the patch.

Expand Down
61 changes: 61 additions & 0 deletions src/SiteCheck.Core/AdvertisedMatch.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// CODEMAP FILE: src/SiteCheck.Core/AdvertisedMatch.cs
// Product: Rampart (oli-site-check) — read-only public-surface hostname checker, public pin 1.9.0
// Role: Shared false-positive guards for enterprise portal cards. A 2xx/3xx on an allowlisted path is not enough; the response must look like that product. Marketing HTML that names the product is not advertisement.
// Called by: PeopleSoftSurface, EnterpriseSurface.
// Calls: FileHit.
// Invariants: GET/HEAD only. Next.js brochure shells (__NEXT_DATA__) are not portals. Trailing-slash redirects of the same path are not portals.
// Sisters: Bastion (bastion-hardening) hardens the local Windows PC. bastion-web is the public storefront and hosts /rampart plus the GitHub asset redirect.
// Map: docs/CODEMAP.md — read that file first for the run/load graph.

namespace SiteCheck.Core;

/// <summary>
/// See docs/CODEMAP.md.
/// Distinguishes a live portal from a brochure that writes about that portal.
/// </summary>
public static class AdvertisedMatch
{
public static bool ContainsAny(string? text, IEnumerable<string> needles)
{
if (string.IsNullOrEmpty(text)) return false;
foreach (var n in needles)
{
if (n.Length == 0) continue;
if (text.Contains(n, StringComparison.OrdinalIgnoreCase)) return true;
}
return false;
}

public static bool IsGenericWebAppShell(string? body) =>
ContainsAny(body, ["__NEXT_DATA__", "/_next/static/"]);

public static bool IsSamePathRedirect(string requestPath, string? location)
{
if (string.IsNullOrWhiteSpace(location)) return false;
if (!Uri.TryCreate(location, UriKind.RelativeOrAbsolute, out var uri)) return false;
var locPath = uri.IsAbsoluteUri ? uri.AbsolutePath : location.Split('?', '#')[0];
var a = TrimSlash(requestPath);
var b = TrimSlash(locPath);
var query = uri.IsAbsoluteUri ? uri.Query : (location.Contains('?', StringComparison.Ordinal) ? location[location.IndexOf('?', StringComparison.Ordinal)..] : "");
return a.Equals(b, StringComparison.OrdinalIgnoreCase) && string.IsNullOrEmpty(query);
}

public static bool PathLooksLike(FileHit hit, IEnumerable<string> needles)
{
if (hit.Status is not (>= 200 and < 400)) return false;
var list = needles as string[] ?? needles.ToArray();
if (ContainsAny(hit.Body, list))
{
if (IsGenericWebAppShell(hit.Body)) return false;
return true;
}
if (IsSamePathRedirect(hit.Path, hit.Location)) return false;
return ContainsAny(hit.Location, list);
}

private static string TrimSlash(string path)
{
var t = path.TrimEnd('/');
return t.Length == 0 ? "/" : t;
}
}
3 changes: 2 additions & 1 deletion src/SiteCheck.Core/Checker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -797,7 +797,8 @@ private static async Task<IReadOnlyList<FileHit>> ProbeNamedPathsAsync(
request.Headers.Host = hostname;
using var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, ct);
var body = await ReadCappedBodyAsync(response, cap, ct);
return new FileHit(path, (int)response.StatusCode, body);
var location = response.Headers.Location?.ToString();
return new FileHit(path, (int)response.StatusCode, body, location);
}
catch (OperationCanceledException) when (ct.IsCancellationRequested)
{
Expand Down
Loading
Loading