update
Some checks failed
Build Quartz Notes / build (push) Failing after 21s

This commit is contained in:
2026-06-10 20:00:45 +01:00
parent 5d3ed14275
commit 9645ee23b0
295 changed files with 2044 additions and 33 deletions

0
Career/Microlise/APIM.md Executable file → Normal file
View File

0
Career/Microlise/ESS ESP/ESP Applications.md Executable file → Normal file
View File

0
Career/Microlise/ESS ESP/ESP Database.md Executable file → Normal file
View File

0
Career/Microlise/ESS ESP/ESP Deploy Stages.md Executable file → Normal file
View File

0
Career/Microlise/ESS ESP/ESP Glossary.md Executable file → Normal file
View File

0
Career/Microlise/ESS ESP/ESP Known Issues and Risks.md Executable file → Normal file
View File

0
Career/Microlise/ESS ESP/ESP Manifest.md Executable file → Normal file
View File

0
Career/Microlise/ESS ESP/ESP OQ.md Executable file → Normal file
View File

0
Career/Microlise/ESS ESP/ESP Pipeline.md Executable file → Normal file
View File

0
Career/Microlise/ESS ESP/ESP Scripts.md Executable file → Normal file
View File

0
Career/Microlise/ESS ESP/ESS ESP Index.md Executable file → Normal file
View File

0
Career/Microlise/ESS ESP/Microlise - ESS.md Executable file → Normal file
View File

0
Career/Microlise/Microlise Assessment.md Executable file → Normal file
View File

0
Career/Microlise/Microlise MOC.md Executable file → Normal file
View File

0
Career/Microlise/Pre work prep.md Executable file → Normal file
View File

0
Career/Microlise/Seb Search Improvements.md Executable file → Normal file
View File

View File

@@ -0,0 +1,801 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Session Stored XSS PENTEST.html</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
</head>
<body>
<h1 id="links">Links:</h1>
<ul>
<li>[[Cross Site Scripting (XSS)]]</li>
</ul>
<h1 id="metadata">Metadata</h1>
<ul>
<li>Name<br />
Understanding Session XSS</li>
<li>Overview<br />
Pages 3334 document an informational “Session stored XSS” finding on
the TMC Schedule Execution Board. The issue is real (unescaped user
input in a JavaScript context) but impact is limited because only the
submitting users session is affected—classic self-XSS, not cross-user
attack.</li>
</ul>
<h2 id="todos">Todos</h2>
<ul class="task-list">
<li><label><input type="checkbox" checked="" />review-finding<br />
Read pages 3334 and map finding to SaveSearchCriteriaToSession +
ScheduleExecutionBoard.aspx flow</label></li>
<li><label><input type="checkbox" checked="" />locate-source<br />
Open TMC Web Portal repo and find session save + inline script render
for date/orderID/time</label></li>
<li><label><input type="checkbox" />remediate-encode<br />
Apply HttpUtility.JavaScriptStringEncode to all session values in
SetupControls() (~1849-1879)</label></li>
<li><label><input type="checkbox" />remediate-validate<br />
Add server-side validation in SaveSearchCriteriaToSession before writing
SEBSessionState</label></li>
<li><label><input type="checkbox" />remediate-retest<br />
Retest with direct POST payload + normal UI search flow on
ScheduleExecutionBoard</label></li>
</ul>
<h1
id="understanding-the-session-stored-xss-finding-pages-3334">Understanding
the Session Stored XSS Finding (Pages 3334)</h1>
<h2 id="where-this-sits-in-the-report">Where this sits in the
report</h2>
<p>The <a
href="d:/_dev/_misc/Pentest-04-26/Microlise%20TMC%20PO%20WA%20April%202026%20v1.0.pdf">Microlise
TMC PO WA April 2026 v1.0.pdf</a> lists <strong>14 findings</strong>
total. Pages 3334 (<a
href="d:/_dev/_misc/Pentest-04-26/33-34.pdf">33-34.pdf</a>) are the last
technical finding before “END OF DOCUMENT”:</p>
<table>
<colgroup>
<col style="width: 10%" />
<col style="width: 89%" />
</colgroup>
<thead>
<tr>
<th>Field</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Title</td>
<td><strong>Session stored XSS</strong></td>
</tr>
<tr>
<td>Severity</td>
<td><strong>Informational</strong> (lowest tier; 4 informational
findings in the report)</td>
</tr>
<tr>
<td>Status</td>
<td>Open</td>
</tr>
<tr>
<td>CWE</td>
<td><a href="https://cwe.mitre.org/data/definitions/79.html">CWE-79</a>
— Improper Neutralization of Input</td>
</tr>
<tr>
<td>Environment</td>
<td><code>cert.microlise.com</code> (cert/UAT), path prefix
<code>/PENTEST/TMCWebPortal/</code></td>
</tr>
</tbody>
</table>
<p>Higher-severity items in the same report (SQLi, IDOR, BFLA, etc.) are
separate; this finding is documented as <strong>technically valid but
low business risk</strong>.</p>
<hr />
<h2 id="what-xss-is-general">What XSS is (general)</h2>
<p><strong>Cross-Site Scripting (XSS)</strong> means untrusted data ends
up in a web page in a way the <strong>browser treats as executable
JavaScript</strong>, instead of inert text.</p>
<p>The name “cross-site” is historical: classic attacks trick a
<strong>victim</strong> into loading a page on <strong>your</strong> app
so script runs in <strong>your</strong> origin (stealing session
cookies, performing actions as the user, etc.).</p>
<p>Common types:</p>
<table>
<colgroup>
<col style="width: 15%" />
<col style="width: 45%" />
<col style="width: 39%" />
</colgroup>
<thead>
<tr>
<th>Type</th>
<th>Persistence</th>
<th>Typical delivery</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Reflected</strong></td>
<td>Not stored; one-off response</td>
<td>Malicious link/query param</td>
</tr>
<tr>
<td><strong>Stored</strong></td>
<td>Saved server-side (DB, file, session)</td>
<td>Victim loads a normal page later</td>
</tr>
<tr>
<td><strong>DOM-based</strong></td>
<td>Client-side only</td>
<td>Unsafe innerHTML, eval, etc.</td>
</tr>
</tbody>
</table>
<p><strong>Defense in depth:</strong> validate input on the server
(whitelist formats), and <strong>encode output</strong> for the exact
context (HTML, attribute, JavaScript string, URL).</p>
<hr />
<h2 id="what-happened-in-this-finding-tmc-context">What happened in
<em>this</em> finding (TMC context)</h2>
<h3 id="affected-surface-source-located">Affected surface (source
located)</h3>
<table>
<colgroup>
<col style="width: 20%" />
<col style="width: 79%" />
</colgroup>
<thead>
<tr>
<th>Role</th>
<th>Path</th>
</tr>
</thead>
<tbody>
<tr>
<td>Page + inline JS</td>
<td><a
href="d:/_dev/WebPortal/src/code/AmberWebUI/SEB/ScheduleExecutionBoard.aspx">ScheduleExecutionBoard.aspx</a></td>
</tr>
<tr>
<td>WebMethod + page properties</td>
<td><a
href="d:/_dev/WebPortal/src/code/AmberWebUI/SEB/ScheduleExecutionBoard.aspx.cs">ScheduleExecutionBoard.aspx.cs</a></td>
</tr>
<tr>
<td>Session storage</td>
<td><a
href="d:/_dev/WebPortal/src/code/AmberWebUI/SEB/SEBSessionState.cs">SEBSessionState.cs</a></td>
</tr>
</tbody>
</table>
<ul>
<li><strong>Endpoint:</strong> ASP.NET <code>[WebMethod]</code>
<code>SaveSearchCriteriaToSession</code> on
<code>ScheduleExecutionBoard.aspx</code></li>
<li><strong>Parameters:</strong> JSON fields <code>date</code>,
<code>orderID</code>, <code>time</code> (also <code>searchID</code>,
<code>hours</code>, <code>quickSearch</code> in the same flow)</li>
<li><strong>Host (pentest):</strong> <code>cert.microlise.com</code>,
path <code>/PENTEST/TMCWebPortal/SEB/...</code></li>
</ul>
<h3 id="attack-flow-as-tested">Attack flow (as tested)</h3>
<pre class="mermaid"><code>flowchart LR
subgraph submit [Step1_Submit]
A[Tester sends POST directly]
B[SaveSearchCriteriaToSession]
C[Values stored in server session]
end
subgraph render [Step2_Render]
D[User loads ScheduleExecutionBoard.aspx]
E[Server embeds session values in script block]
F[Browser executes unescaped JS]
end
A --&gt; B --&gt; C
C --&gt; D --&gt; E --&gt; F</code></pre>
<ol type="1">
<li><strong>Save:</strong> User (or tester) POSTs JSON to
<code>SaveSearchCriteriaToSession</code>. The app saves search criteria
into the <strong>server-side session</strong>.</li>
<li><strong>Render:</strong> On the next load of
<code>ScheduleExecutionBoard.aspx</code>, those values are written into
the HTML <strong>inside a <code>&lt;script&gt;</code> block</strong>, as
JavaScript string literals.</li>
<li><strong>Bug:</strong> Values are inserted <strong>without JavaScript
string encoding</strong>. A crafted <code>date</code> can <strong>break
out of the string</strong> and run arbitrary JS.</li>
<li><strong>Proof:</strong> Pentesters confirmed execution in the
browser; screenshots in the PDF show the POST and page source.</li>
</ol>
<h3 id="code-path-matches-report-exactly">Code path (matches report
exactly)</h3>
<p><strong>1. Save — no server-side validation</strong></p>
<div class="sourceCode" id="cb2"><pre
class="sourceCode csharp"><code class="sourceCode cs"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co">// ScheduleExecutionBoard.aspx.cs lines 336-348</span></span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="op">[</span>WebMethod<span class="op">]</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">static</span> <span class="dt">void</span> <span class="fu">SaveSearchCriteriaToSession</span><span class="op">(</span><span class="dt">string</span> searchID<span class="op">,</span> <span class="dt">string</span> orderID<span class="op">,</span> <span class="dt">string</span> date<span class="op">,</span> <span class="dt">string</span> time<span class="op">,</span> <span class="dt">int</span> hours<span class="op">,</span> <span class="dt">bool</span> displayPriorityJourneys<span class="op">,</span> <span class="dt">string</span> quickSearch<span class="op">)</span></span>
<span id="cb2-4"><a href="#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb2-5"><a href="#cb2-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">var</span> sebState <span class="op">=</span> <span class="kw">new</span> <span class="fu">SEBSessionState</span><span class="op">();</span></span>
<span id="cb2-6"><a href="#cb2-6" aria-hidden="true" tabindex="-1"></a> sebState<span class="op">.</span><span class="fu">ComplexSearch</span> <span class="op">=</span> searchID<span class="op">;</span></span>
<span id="cb2-7"><a href="#cb2-7" aria-hidden="true" tabindex="-1"></a> sebState<span class="op">.</span><span class="fu">OrderBy</span> <span class="op">=</span> orderID<span class="op">;</span></span>
<span id="cb2-8"><a href="#cb2-8" aria-hidden="true" tabindex="-1"></a> sebState<span class="op">.</span><span class="fu">SearchDate</span> <span class="op">=</span> date<span class="op">;</span></span>
<span id="cb2-9"><a href="#cb2-9" aria-hidden="true" tabindex="-1"></a> sebState<span class="op">.</span><span class="fu">SearchTime</span> <span class="op">=</span> time<span class="op">;</span></span>
<span id="cb2-10"><a href="#cb2-10" aria-hidden="true" tabindex="-1"></a> <span class="co">// ...</span></span>
<span id="cb2-11"><a href="#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p><strong>2. Persist — per-user ASP.NET session</strong></p>
<p><a
href="d:/_dev/WebPortal/src/code/AmberWebUI/SEB/SEBSessionState.cs">SEBSessionState.cs</a>
stores values under keys <code>dateID</code>, <code>timeID</code>,
<code>orderByID</code>.</p>
<p><strong>3. Load — on next full page GET</strong></p>
<div class="sourceCode" id="cb3"><pre
class="sourceCode csharp"><code class="sourceCode cs"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">// ScheduleExecutionBoard.aspx.cs lines 267-277</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a><span class="kw">private</span> <span class="dt">void</span> <span class="fu">SetupControls</span><span class="op">()</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">var</span> sebState <span class="op">=</span> <span class="kw">new</span> <span class="fu">SEBSessionState</span><span class="op">();</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> SessionOrderID <span class="op">=</span> sebState<span class="op">.</span><span class="fu">OrderBy</span><span class="op">;</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> SessionDate <span class="op">=</span> sebState<span class="op">.</span><span class="fu">SearchDate</span><span class="op">;</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a> SessionTime <span class="op">=</span> sebState<span class="op">.</span><span class="fu">SearchTime</span><span class="op">;</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a> <span class="co">// ...</span></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p><strong>4. Render — vulnerable inline JavaScript (root
cause)</strong></p>
<div class="sourceCode" id="cb4"><pre
class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="co">// ScheduleExecutionBoard.aspx lines 1853-1875</span></span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> (<span class="st">&quot;&lt;%=SessionDate%&gt;&quot;</span>) {</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a> <span class="fu">$</span>(<span class="st">&#39;#txtStart&#39;</span>)<span class="op">.</span><span class="fu">val</span>(<span class="st">&quot;&lt;%= SessionDate %&gt;&quot;</span>)<span class="op">;</span></span>
<span id="cb4-4"><a href="#cb4-4" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb4-5"><a href="#cb4-5" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> (<span class="st">&quot;&lt;%=SessionTime%&gt;&quot;</span>) {</span>
<span id="cb4-6"><a href="#cb4-6" aria-hidden="true" tabindex="-1"></a> <span class="fu">$</span>(<span class="st">&#39;#inputtime&#39;</span>)<span class="op">.</span><span class="fu">val</span>(<span class="st">&quot;&lt;%=SessionTime%&gt;&quot;</span>)<span class="op">;</span></span>
<span id="cb4-7"><a href="#cb4-7" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb4-8"><a href="#cb4-8" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> (<span class="st">&quot;&lt;%=SessionOrderID%&gt;&quot;</span>) {</span>
<span id="cb4-9"><a href="#cb4-9" aria-hidden="true" tabindex="-1"></a> <span class="fu">$</span>(orderBySelector <span class="op">+</span> <span class="st">&#39; option[value=&quot;&lt;%=SessionOrderID%&gt;&quot;]&#39;</span>)<span class="op">.</span><span class="fu">attr</span>(<span class="st">&#39;selected&#39;</span><span class="op">,</span> <span class="st">&#39;selected&#39;</span>)<span class="op">;</span></span>
<span id="cb4-10"><a href="#cb4-10" aria-hidden="true" tabindex="-1"></a>}</span></code></pre></div>
<p>Example payload in session:
<code>"); alert(document.domain); //</code></p>
<div class="sourceCode" id="cb5"><pre
class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="fu">$</span>(<span class="st">&#39;#txtStart&#39;</span>)<span class="op">.</span><span class="fu">val</span>(<span class="st">&quot;&quot;</span>)<span class="op">;</span> <span class="fu">alert</span>(<span class="bu">document</span><span class="op">.</span><span class="at">domain</span>)<span class="op">;</span> <span class="co">//&quot;);</span></span></code></pre></div>
<p><strong>Related:</strong> <code>QuickSearch</code> at line ~1879 —
fix in the same pass.</p>
<h3 id="why-stored-session-and-self-xss">Why “stored”, “session”, and
“self-XSS”</h3>
<ul>
<li><strong>Stored:</strong> Payload survives page navigation in
<strong>session state</strong>.</li>
<li><strong>Self-XSS:</strong> Only the submitters session is affected;
no normal cross-user path.</li>
<li>Severity <strong>Informational</strong> because threat model is weak
vs shared stored XSS.</li>
</ul>
<h3 id="client-vs-server-validation-gap">Client vs server validation
gap</h3>
<p>Pentesters bypassed browser validation via direct POST. No
server-side validation blocked arbitrary strings.</p>
<hr />
<h2 id="replicating-the-vulnerability-hands-on">Replicating the
vulnerability (hands-on)</h2>
<p>Use this section to <strong>see the bug work</strong> on an
authorized environment (e.g. cert/UAT), then <strong>repeat the same
steps after fixes</strong> and compare outcomes.</p>
<h3 id="prerequisites">Prerequisites</h3>
<table>
<colgroup>
<col style="width: 20%" />
<col style="width: 80%" />
</colgroup>
<thead>
<tr>
<th>Requirement</th>
<th>Detail</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Authorization</strong></td>
<td>Pentest scope or internal security test policy only</td>
</tr>
<tr>
<td><strong>Permission</strong></td>
<td><code>Microlise:TMC:SEB:Read</code></td>
</tr>
<tr>
<td><strong>URL</strong></td>
<td>e.g. <code>https://&lt;host&gt;/TMCWebPortal/SEB/ScheduleExecutionBoard.aspx</code></td>
</tr>
<tr>
<td><strong>Tools</strong></td>
<td>Browser + DevTools or Burp Suite</td>
</tr>
<tr>
<td><strong>Build</strong></td>
<td>Before-fix build first; redeploy with remediation for after-fix
runs</td>
</tr>
</tbody>
</table>
<p>Must be logged in (valid session cookie on POST).</p>
<h3 id="what-you-should-observe-before-fix">What you should observe
(before fix)</h3>
<pre class="mermaid"><code>sequenceDiagram
participant You as Tester_browser
participant API as SaveSearchCriteriaToSession
participant Sess as ASP.NET_session
participant Page as ScheduleExecutionBoard_GET
You-&gt;&gt;API: POST JSON with malicious date
API-&gt;&gt;Sess: Store raw date in session
You-&gt;&gt;Page: Reload SEB page
Page-&gt;&gt;You: HTML with unescaped date inside script
You-&gt;&gt;You: alert or other JS runs</code></pre>
<ol type="1">
<li>WebMethod returns HTTP 200.</li>
<li>Payload never went through <code>DateValidation()</code>.</li>
<li>Full page reload → JS runs (e.g. <code>alert</code>).</li>
<li>View Source: payload inside double-quoted JS string, unescaped.</li>
</ol>
<p><strong>Self-XSS:</strong> only your session is poisoned.</p>
<h3 id="step-by-step-reproduction">Step-by-step reproduction</h3>
<p><strong>Step 1 — Baseline (optional)</strong></p>
<ol type="1">
<li>Open SEB, perform a search.</li>
<li>DevTools → Network → <code>SaveSearchCriteriaToSession</code>.</li>
<li>Note POST, <code>application/json</code>, body shape, Cookie
header.</li>
</ol>
<p><strong>Step 2 — Inject via direct POST (bypass UI)</strong></p>
<table>
<thead>
<tr>
<th>Parameter</th>
<th>Suggested test value</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>searchID</code></td>
<td><code>0.X</code></td>
</tr>
<tr>
<td><code>orderID</code></td>
<td><code>0.X</code></td>
</tr>
<tr>
<td><code>date</code></td>
<td><code>"); alert(document.domain);//</code></td>
</tr>
<tr>
<td><code>time</code></td>
<td><code>00:00</code></td>
</tr>
<tr>
<td><code>hours</code></td>
<td><code>24</code></td>
</tr>
<tr>
<td><code>displayPriorityJourneys</code></td>
<td><code>false</code></td>
</tr>
<tr>
<td><code>quickSearch</code></td>
<td><code>""</code></td>
</tr>
</tbody>
</table>
<p><strong>Burp:</strong> Repeater → replace JSON body → send.</p>
<p><strong>Browser console</strong> (on SEB page, same origin):</p>
<div class="sourceCode" id="cb7"><pre
class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="fu">fetch</span>(<span class="st">&#39;ScheduleExecutionBoard.aspx/SaveSearchCriteriaToSession&#39;</span><span class="op">,</span> {</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">method</span><span class="op">:</span> <span class="st">&#39;POST&#39;</span><span class="op">,</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">credentials</span><span class="op">:</span> <span class="st">&#39;include&#39;</span><span class="op">,</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">headers</span><span class="op">:</span> { <span class="st">&#39;Content-Type&#39;</span><span class="op">:</span> <span class="st">&#39;application/json; charset=utf-8&#39;</span> }<span class="op">,</span></span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">body</span><span class="op">:</span> <span class="bu">JSON</span><span class="op">.</span><span class="fu">stringify</span>({</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">searchID</span><span class="op">:</span> <span class="st">&#39;0.X&#39;</span><span class="op">,</span></span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">orderID</span><span class="op">:</span> <span class="st">&#39;0.X&#39;</span><span class="op">,</span></span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">date</span><span class="op">:</span> <span class="st">&#39;&quot;); alert(document.domain);//&#39;</span><span class="op">,</span></span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">time</span><span class="op">:</span> <span class="st">&#39;00:00&#39;</span><span class="op">,</span></span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a> <span class="dt">hours</span><span class="op">:</span> <span class="dv">24</span><span class="op">,</span></span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a> <span class="dt">displayPriorityJourneys</span><span class="op">:</span> <span class="kw">false</span><span class="op">,</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a> <span class="dt">quickSearch</span><span class="op">:</span> <span class="st">&#39;&#39;</span></span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a> })</span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a>})<span class="op">.</span><span class="fu">then</span>(r <span class="kw">=&gt;</span> <span class="bu">console</span><span class="op">.</span><span class="fu">log</span>(<span class="st">&#39;status&#39;</span><span class="op">,</span> r<span class="op">.</span><span class="at">status</span>))<span class="op">;</span></span></code></pre></div>
<p><strong>curl</strong> (replace host, path, cookies):</p>
<div class="sourceCode" id="cb8"><pre
class="sourceCode bash"><code class="sourceCode bash"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="ex">curl</span> <span class="at">-s</span> <span class="at">-o</span> /dev/null <span class="at">-w</span> <span class="st">&quot;%{http_code}&quot;</span> <span class="dt">\</span></span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> <span class="at">-X</span> POST <span class="st">&quot;https://&lt;host&gt;/&lt;TMCWebPortal&gt;/SEB/ScheduleExecutionBoard.aspx/SaveSearchCriteriaToSession&quot;</span> <span class="dt">\</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a> <span class="at">-H</span> <span class="st">&quot;Content-Type: application/json; charset=utf-8&quot;</span> <span class="dt">\</span></span>
<span id="cb8-4"><a href="#cb8-4" aria-hidden="true" tabindex="-1"></a> <span class="at">-H</span> <span class="st">&quot;Cookie: &lt;paste-session-cookies&gt;&quot;</span> <span class="dt">\</span></span>
<span id="cb8-5"><a href="#cb8-5" aria-hidden="true" tabindex="-1"></a> <span class="at">-d</span> <span class="st">&quot;{</span><span class="dt">\&quot;</span><span class="st">searchID</span><span class="dt">\&quot;</span><span class="st">:</span><span class="dt">\&quot;</span><span class="st">0.X</span><span class="dt">\&quot;</span><span class="st">,</span><span class="dt">\&quot;</span><span class="st">orderID</span><span class="dt">\&quot;</span><span class="st">:</span><span class="dt">\&quot;</span><span class="st">0.X</span><span class="dt">\&quot;</span><span class="st">,</span><span class="dt">\&quot;</span><span class="st">date</span><span class="dt">\&quot;</span><span class="st">:</span><span class="dt">\&quot;\\\&quot;</span><span class="st">); alert(document.domain);//</span><span class="dt">\&quot;</span><span class="st">,</span><span class="dt">\&quot;</span><span class="st">time</span><span class="dt">\&quot;</span><span class="st">:</span><span class="dt">\&quot;</span><span class="st">00:00</span><span class="dt">\&quot;</span><span class="st">,</span><span class="dt">\&quot;</span><span class="st">hours</span><span class="dt">\&quot;</span><span class="st">:24,</span><span class="dt">\&quot;</span><span class="st">displayPriorityJourneys</span><span class="dt">\&quot;</span><span class="st">:false,</span><span class="dt">\&quot;</span><span class="st">quickSearch</span><span class="dt">\&quot;</span><span class="st">:</span><span class="dt">\&quot;\&quot;</span><span class="st">}&quot;</span></span></code></pre></div>
<p><strong>Step 3 — Trigger render (stored XSS)</strong></p>
<ol type="1">
<li>Full navigation reload of <code>ScheduleExecutionBoard.aspx</code>
(F5).</li>
<li><code>SetupControls()</code> embeds session <code>date</code>
(~lines 18541855).</li>
</ol>
<p><strong>Step 4 — Confirm</strong></p>
<table>
<colgroup>
<col style="width: 19%" />
<col style="width: 80%" />
</colgroup>
<thead>
<tr>
<th>Check</th>
<th>Before fix (expected)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Popup / console</td>
<td><code>alert(document.domain)</code> runs</td>
</tr>
<tr>
<td>View Source</td>
<td>Literal <code>"); alert(...)</code> inside
<code>$('#txtStart').val("...")</code> unescaped</td>
</tr>
<tr>
<td>Network on reload</td>
<td>Normal GET only; XSS from inline script</td>
</tr>
<tr>
<td>Other users</td>
<td>No effect (different session)</td>
</tr>
</tbody>
</table>
<p><strong>Step 5 — Optional:</strong> malicious <code>time</code> or
<code>orderID</code>.</p>
<p><strong>Step 6 — Clean up:</strong> log out/in or POST valid
date/time.</p>
<h3 id="comparison-matrix-before-vs-after-fixes">Comparison matrix
(before vs after fixes)</h3>
<p>Run the same Steps 24 after each change:</p>
<table>
<colgroup>
<col style="width: 33%" />
<col style="width: 10%" />
<col style="width: 20%" />
<col style="width: 22%" />
<col style="width: 13%" />
</colgroup>
<thead>
<tr>
<th>Observation</th>
<th>Before fix</th>
<th>After encoding only</th>
<th>After validation only</th>
<th>After both</th>
</tr>
</thead>
<tbody>
<tr>
<td>POST malicious <code>date</code> accepted?</td>
<td>Yes (200)</td>
<td>Yes (200)</td>
<td>No / not stored</td>
<td>No</td>
</tr>
<tr>
<td><code>alert</code> on reload?</td>
<td><strong>Yes</strong></td>
<td><strong>No</strong></td>
<td>Depends*</td>
<td><strong>No</strong></td>
</tr>
<tr>
<td>Executable JS in View Source?</td>
<td><strong>Yes</strong></td>
<td><strong>No</strong> (escaped)</td>
<td>Depends*</td>
<td><strong>No</strong></td>
</tr>
<tr>
<td><code>#txtStart</code> shows attack text?</td>
<td>Maybe</td>
<td>Escaped/safe</td>
<td>Default/empty</td>
<td>Default/empty</td>
</tr>
<tr>
<td>Normal UI search + reload works?</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes</td>
</tr>
</tbody>
</table>
<p>*If only validation: reload may show no XSS without encoding — still
apply both fixes.</p>
<h3 id="why-ui-only-testing-misses-the-bug">Why UI-only testing misses
the bug</h3>
<table>
<colgroup>
<col style="width: 35%" />
<col style="width: 32%" />
<col style="width: 32%" />
</colgroup>
<thead>
<tr>
<th>Path</th>
<th><code>DateValidation()</code> runs?</th>
<th>Payload reaches session?</th>
</tr>
</thead>
<tbody>
<tr>
<td>Click Search in UI</td>
<td>Yes</td>
<td>No (normal typing)</td>
</tr>
<tr>
<td>Direct POST / Burp / fetch</td>
<td><strong>No</strong></td>
<td><strong>Yes</strong></td>
</tr>
</tbody>
</table>
<p>Reproduction <strong>must</strong> use direct POST to match the
pentest.</p>
<h3 id="evidence-to-capture-for-fix-sign-off">Evidence to capture (for
fix sign-off)</h3>
<ol type="1">
<li>Request (POST body with payload).</li>
<li>Screenshot of alert (before) or no alert (after).</li>
<li>View Source snippet around <code>$('#txtStart').val(</code>.</li>
<li>Regression: legitimate date, reload, criteria restored.</li>
</ol>
<h3 id="safety-and-scope">Safety and scope</h3>
<ul>
<li>No production without approval.</li>
<li>Prefer <code>alert(document.domain)</code> over exfiltration
demos.</li>
<li>Self-XSS: coding defect demo, not mass compromise.</li>
</ul>
<hr />
<h2 id="how-to-fix-it">How to fix it</h2>
<p>Use <strong>two layers</strong>: output encoding + server-side
validation.</p>
<h3 id="fix-1-output-encoding-required">Fix 1 — Output encoding
(required)</h3>
<p>File: <a
href="d:/_dev/WebPortal/src/code/AmberWebUI/SEB/ScheduleExecutionBoard.aspx">ScheduleExecutionBoard.aspx</a>,
<code>SetupControls()</code> (~18471884).</p>
<table>
<thead>
<tr>
<th>Line (approx)</th>
<th>Field</th>
<th>Encode</th>
</tr>
</thead>
<tbody>
<tr>
<td>18491850</td>
<td>SessionSearchID</td>
<td>Yes</td>
</tr>
<tr>
<td>18541855</td>
<td>SessionDate</td>
<td>Yes</td>
</tr>
<tr>
<td>18601861</td>
<td>SessionTime</td>
<td>Yes</td>
</tr>
<tr>
<td>18721874</td>
<td>SessionOrderID</td>
<td>Yes</td>
</tr>
<tr>
<td>18781879</td>
<td>QuickSearch</td>
<td>Yes</td>
</tr>
</tbody>
</table>
<p><strong>Before:</strong></p>
<div class="sourceCode" id="cb9"><pre
class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="fu">$</span>(<span class="st">&#39;#txtStart&#39;</span>)<span class="op">.</span><span class="fu">val</span>(<span class="st">&quot;&lt;%= SessionDate %&gt;&quot;</span>)<span class="op">;</span></span></code></pre></div>
<p><strong>After:</strong></p>
<div class="sourceCode" id="cb10"><pre
class="sourceCode javascript"><code class="sourceCode javascript"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="fu">$</span>(<span class="st">&#39;#txtStart&#39;</span>)<span class="op">.</span><span class="fu">val</span>(<span class="st">&quot;&lt;%= HttpUtility.JavaScriptStringEncode(SessionDate ?? string.Empty) %&gt;&quot;</span>)<span class="op">;</span></span></code></pre></div>
<p>Encode <code>if</code> guards too, or use code-behind booleans
(<code>HasSessionDate</code>).</p>
<h3 id="fix-2-server-side-input-validation">Fix 2 — Server-side input
validation</h3>
<p>File: <a
href="d:/_dev/WebPortal/src/code/AmberWebUI/SEB/ScheduleExecutionBoard.aspx.cs">ScheduleExecutionBoard.aspx.cs</a>,
<code>SaveSearchCriteriaToSession</code> (~337).</p>
<table>
<thead>
<tr>
<th>Parameter</th>
<th>Validation rule</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>date</code></td>
<td>Same regex as client <code>DateValidation()</code></td>
</tr>
<tr>
<td><code>time</code></td>
<td><code>^[0-2][0-9]:[0-5][0-9]$</code></td>
</tr>
<tr>
<td><code>orderID</code></td>
<td><code>^[0-9]+(\.X)?$</code></td>
</tr>
<tr>
<td><code>searchID</code></td>
<td>Same as <code>orderID</code></td>
</tr>
<tr>
<td><code>hours</code></td>
<td>Clamp 1999</td>
</tr>
</tbody>
</table>
<div class="sourceCode" id="cb11"><pre
class="sourceCode csharp"><code class="sourceCode cs"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="op">[</span>WebMethod<span class="op">]</span></span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a><span class="kw">public</span> <span class="kw">static</span> <span class="dt">void</span> <span class="fu">SaveSearchCriteriaToSession</span><span class="op">(...)</span></span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a><span class="op">{</span></span>
<span id="cb11-4"><a href="#cb11-4" aria-hidden="true" tabindex="-1"></a> <span class="kw">if</span> <span class="op">(!</span><span class="fu">IsValidSebDate</span><span class="op">(</span>date<span class="op">)</span> <span class="op">||</span> <span class="op">!</span><span class="fu">IsValidSebTime</span><span class="op">(</span>time<span class="op">)</span></span>
<span id="cb11-5"><a href="#cb11-5" aria-hidden="true" tabindex="-1"></a> <span class="op">||</span> <span class="op">!</span><span class="fu">IsValidQueryComponentId</span><span class="op">(</span>orderID<span class="op">)</span> <span class="op">||</span> <span class="op">!</span><span class="fu">IsValidQueryComponentId</span><span class="op">(</span>searchID<span class="op">))</span></span>
<span id="cb11-6"><a href="#cb11-6" aria-hidden="true" tabindex="-1"></a> <span class="op">{</span></span>
<span id="cb11-7"><a href="#cb11-7" aria-hidden="true" tabindex="-1"></a> <span class="kw">return</span><span class="op">;</span></span>
<span id="cb11-8"><a href="#cb11-8" aria-hidden="true" tabindex="-1"></a> <span class="op">}</span></span>
<span id="cb11-9"><a href="#cb11-9" aria-hidden="true" tabindex="-1"></a> <span class="dt">var</span> sebState <span class="op">=</span> <span class="kw">new</span> <span class="fu">SEBSessionState</span><span class="op">();</span></span>
<span id="cb11-10"><a href="#cb11-10" aria-hidden="true" tabindex="-1"></a> <span class="co">// ...</span></span>
<span id="cb11-11"><a href="#cb11-11" aria-hidden="true" tabindex="-1"></a><span class="op">}</span></span></code></pre></div>
<p><strong>Date regex:</strong></p>
<pre><code>^[0-9]{4}-(((0[13578]|(10|12))-(0[1-9]|[1-2][0-9]|3[0-1]))|(02-(0[1-9]|[1-2][0-9]))|((0[469]|11)-(0[1-9]|[1-2][0-9]|30)))$</code></pre>
<h3 id="fix-3-what-not-to-do">Fix 3 — What not to do</h3>
<ul>
<li>Do not rely on client <code>DateValidation()</code> alone.</li>
<li>Do not use <code>HtmlEncode</code> in JS string literals.</li>
<li>Do not use <code>innerHTML</code>; keep <code>.val()</code>.</li>
</ul>
<h3 id="fix-4-verification-test-plan">Fix 4 — Verification / test
plan</h3>
<p>Master procedure: <strong>Replicating the vulnerability</strong>
section above.</p>
<p><strong>Negative test:</strong></p>
<div class="sourceCode" id="cb13"><pre
class="sourceCode json"><code class="sourceCode json"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="fu">{</span></span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;searchID&quot;</span><span class="fu">:</span> <span class="st">&quot;0.X&quot;</span><span class="fu">,</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;orderID&quot;</span><span class="fu">:</span> <span class="st">&quot;0.X&quot;</span><span class="fu">,</span></span>
<span id="cb13-4"><a href="#cb13-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;date&quot;</span><span class="fu">:</span> <span class="st">&quot;</span><span class="ch">\&quot;</span><span class="st">); alert(1);//&quot;</span><span class="fu">,</span></span>
<span id="cb13-5"><a href="#cb13-5" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;time&quot;</span><span class="fu">:</span> <span class="st">&quot;00:00&quot;</span><span class="fu">,</span></span>
<span id="cb13-6"><a href="#cb13-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;hours&quot;</span><span class="fu">:</span> <span class="dv">24</span><span class="fu">,</span></span>
<span id="cb13-7"><a href="#cb13-7" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;displayPriorityJourneys&quot;</span><span class="fu">:</span> <span class="kw">false</span><span class="fu">,</span></span>
<span id="cb13-8"><a href="#cb13-8" aria-hidden="true" tabindex="-1"></a> <span class="dt">&quot;quickSearch&quot;</span><span class="fu">:</span> <span class="st">&quot;&quot;</span></span>
<span id="cb13-9"><a href="#cb13-9" aria-hidden="true" tabindex="-1"></a><span class="fu">}</span></span></code></pre></div>
<p><strong>Pass:</strong> No alert; escaped in source; invalid date not
stored (with Fix 2).</p>
<p><strong>Positive test:</strong> UI search + reload restores
criteria.</p>
<h3 id="files-to-change-summary">Files to change (summary)</h3>
<table>
<colgroup>
<col style="width: 70%" />
<col style="width: 29%" />
</colgroup>
<thead>
<tr>
<th>File</th>
<th>Change</th>
</tr>
</thead>
<tbody>
<tr>
<td><a
href="d:/_dev/WebPortal/src/code/AmberWebUI/SEB/ScheduleExecutionBoard.aspx">ScheduleExecutionBoard.aspx</a></td>
<td><code>JavaScriptStringEncode</code> in
<code>SetupControls()</code></td>
</tr>
<tr>
<td><a
href="d:/_dev/WebPortal/src/code/AmberWebUI/SEB/ScheduleExecutionBoard.aspx.cs">ScheduleExecutionBoard.aspx.cs</a></td>
<td>Validation in <code>SaveSearchCriteriaToSession</code></td>
</tr>
</tbody>
</table>
<p><strong>References:</strong> <a
href="https://owasp.org/www-community/attacks/xss/">OWASP XSS</a>, <a
href="https://portswigger.net/web-security/cross-site-scripting/stored">PortSwigger
Stored XSS</a>, <a
href="https://learn.microsoft.com/en-us/dotnet/api/system.web.httputility.javascriptstringencode">JavaScriptStringEncode</a></p>
<hr />
<h2 id="mental-model-severity-vs-correctness">Mental model: severity vs
correctness</h2>
<table>
<thead>
<tr>
<th>Question</th>
<th>Answer</th>
</tr>
</thead>
<tbody>
<tr>
<td>Real coding flaw?</td>
<td><strong>Yes</strong> (CWE-79)</td>
</tr>
<tr>
<td>Cross-user session hijack?</td>
<td><strong>Not under normal use</strong> (self-XSS)</td>
</tr>
<tr>
<td>Should it still be fixed?</td>
<td><strong>Yes</strong>, as hygiene</td>
</tr>
<tr>
<td>Priority vs SQLi / IDOR?</td>
<td><strong>Much lower</strong> (informational)</td>
</tr>
</tbody>
</table>
<h2 id="code-snippets">Code snippets</h2>
<h3 id="reset">Reset:</h3>
<pre><code>fetch(&#39;ScheduleExecutionBoard.aspx/SaveSearchCriteriaToSession&#39;, {
  method: &#39;POST&#39;,
  credentials: &#39;include&#39;,
  headers: { &#39;Content-Type&#39;: &#39;application/json; charset=utf-8&#39; },
  body: JSON.stringify({
    searchID: &#39;0.X&#39;, orderID: &#39;0.X&#39;,
    date: &#39;2026-05-27&#39;, time: &#39;00:00&#39;, hours: 24,
    displayPriorityJourneys: false, quickSearch: &#39;&#39;
  })
}).then(() =&gt; location.reload());</code></pre>
<h3 id="alert">Alert</h3>
<pre><code>fetch(&#39;ScheduleExecutionBoard.aspx/SaveSearchCriteriaToSession&#39;, {
  method: &#39;POST&#39;,
  credentials: &#39;include&#39;,
  headers: { &#39;Content-Type&#39;: &#39;application/json; charset=utf-8&#39; },
  body: JSON.stringify({
    searchID: &#39;0.X&#39;,
    orderID: &#39;0.X&#39;,
    date: &#39;&quot;+alert(1)+&quot;&#39;,
    time: &#39;00:00&#39;,
    hours: 24,
    displayPriorityJourneys: false,
    quickSearch: &#39;&#39;
  })
}).then(r =&gt; console.log(r.status, r.statusText));</code></pre>
<h3 id="poison-session">Poison Session</h3>
<pre><code>function poisonSession(field, payload) {
  const body = {
    searchID: &#39;0.X&#39;,
    orderID: &#39;0.X&#39;,
    date: &#39;2026-05-27&#39;,
    time: &#39;00:00&#39;,
    hours: 24,
    displayPriorityJourneys: false,
    quickSearch: &#39;&#39;
  };
  body[field] = payload;
  return fetch(&#39;ScheduleExecutionBoard.aspx/SaveSearchCriteriaToSession&#39;, {
    method: &#39;POST&#39;,
    credentials: &#39;include&#39;,
    headers: { &#39;Content-Type&#39;: &#39;application/json; charset=utf-8&#39; },
    body: JSON.stringify(body)
  }).then(r =&gt; console.log(field, r.status, r.statusText));
}</code></pre>
<h3 id="confirm-prompt-alternative-dialog-evidence">Confirm / prompt
(alternative dialog evidence)</h3>
<pre><code>poisonSession(&#39;date&#39;, &#39;&quot;+confirm(&quot;XSS: SEB session poisoned&quot;)+&quot;&#39;);</code></pre>
<h3 id="visible-banner">Visible banner</h3>
<pre><code>poisonSession(&#39;date&#39;, &#39;&quot;+document.body.insertAdjacentHTML(&quot;afterbegin&quot;,&quot;&lt;div style=\\&quot;position:fixed;top:0;left:0;right:0;background:red;color:white;z-index:99999;padding:12px;text-align:center\\&quot;&gt;XSS PoC — arbitrary script executed in SEB context&lt;/div&gt;&quot;)+&quot;&#39;);</code></pre>
</body>
</html>

54
Career/Microlise/Session Stored XSS PENTEST.md Executable file → Normal file
View File

@@ -380,6 +380,60 @@ public static void SaveSearchCriteriaToSession(...)
^[0-9]{4}-(((0[13578]|(10|12))-(0[1-9]|[1-2][0-9]|3[0-1]))|(02-(0[1-9]|[1-2][0-9]))|((0[469]|11)-(0[1-9]|[1-2][0-9]|30)))$
#### Explanation of regex:
##### `SebDateRegex`
`^[0-9]{4}-(((0[13578]|(10|12))-(0[1-9]|[1-2][0-9]|3[0-1]))|(02-(0[1-9]|[1-2][0-9]))|((0[469]|11)-(0[1-9]|[1-2][0-9]|30)))$`
Overall shape: `YYYY-MM-DD` only — four digits, hyphen, month/day with structure checks.
|Part|Meaning|
|---|---|
|`^` / `$`|Whole string must match (no extra characters).|
|`[0-9]{4}-`|Four-digit year, then `-`.|
|31-day months|`(0[13578]\|(10\|12))-(0[1-9]\|[1-2][0-9]\|3[0-1])` — Jan, Mar, May, Jul, Aug, Oct, Dec: day `01``31`.|
|February|`02-(0[1-9]\|[1-2][0-9])` — day `01``29` (no Feb 30/31).|
|30-day months|`(0[469]\|11)-(0[1-9]\|[1-2][0-9]\|30)` — Apr, Jun, Sep, Nov: day `01``30`.|
Matches: `2026-05-27`, `2024-02-29`, `2024-04-30`
Rejects: `2026-13-01`, `not-a-date`, `"); alert(1);//`, empty/null
Note: This is format validation, not a full calendar check. It does not prove the date exists (e.g. `2025-02-30` can match the February branch). That matches the existing client `DateValidation()` in `ScheduleExecutionBoard.aspx` (line 4050).
#####  `SebTimeRegex`
`^[0-2][0-9]:[0-5][0-9]$`
Overall shape: two digits, `:`, two digits — same idea as `HH:mm` in the UI.
|Part|Meaning|
|---|---|
|`[0-2][0-9]`|First hour digit 02, second 09 → allows `00``29` (looser than strict 0023).|
|`:`|Literal colon.|
|`[0-5][0-9]`|Minutes `00``59`.|
Matches: `00:00`, `12:30`, `23:59`
Rejects: `25:99`, `9:00` (needs two hour digits), `"); alert(1);//`
Note: Values like `29:00` match the pattern but are not real clock times; the pentest/UI convention is this simple pattern, not full time-of-day logic.
#####  `QueryComponentIdRegex`
`^[0-9]+(\.X)?$`
Overall shape: one or more digits, optionally followed by `.X` — the values SEB puts on search/order-by dropdowns.
|Part|Meaning|
|---|---|
|`^` / `$`|Whole string only.|
|`[0-9]+`|One or more digits (e.g. `0`, `123`).|
|`(\.X)?`|Optional literal `.X` (shared / external query suffix in combo markup).|
Matches: `0`, `0.X`, `123`, `123.X`
Rejects: `abc`, `0.XY`, `"); alert(1);//`, empty/null
This aligns with how items are built in `BuildSearchForComboBoxItems` / `BuildOrderByComboBoxItems` (e.g. `"0.X"` for “all journeys” / unspecified order-by).
### Fix 3 — What not to do
- Do not rely on client `DateValidation()` alone.