This is an automated email from the ASF dual-hosted git repository.

szaszm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git

commit 8dbbd228a1812753fa5fd722cfd77300151bb6a4
Author: Gabor Gyimesi <[email protected]>
AuthorDate: Fri Oct 2 17:32:32 2026 +0200

    MINIFICPP-2914 Fix stale and unescaped data on the CI status page
    
    - Pass cache: 'no-cache' on both API calls, so the browser returns the 
current state.
    - Sort runs by created_at descending instead of trusting runs[0]
    - Read the workflow name from the runs payload instead of making a second 
request per workflow to delay reaching request limits.
    - Escape API-supplied strings before interpolating them into HTML
    
    Fix generated using Claude Code.
    
    Closes #2274
    
    Signed-off-by: Marton Szasz <[email protected]>
---
 docs/status/index.html | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/docs/status/index.html b/docs/status/index.html
index 70724a5e2..f153f7d88 100644
--- a/docs/status/index.html
+++ b/docs/status/index.html
@@ -479,7 +479,7 @@
     try {
       const r = await fetch(
         
`https://api.github.com/repos/${CONFIG.owner}/${CONFIG.repo}/actions/workflows/${file}`,
-        { headers: getHeaders() }
+        { headers: getHeaders(), cache: 'no-cache' }
       );
       if (!r.ok) return null;
       const data = await r.json();
@@ -494,7 +494,7 @@
       try {
         const r = await fetch(
           
`https://api.github.com/repos/${owner}/${repo}/actions/workflows/${file}/runs?per_page=10&branch=${CONFIG.branch}`,
-          { headers: getHeaders() }
+          { headers: getHeaders(), cache: 'no-cache' }
         );
         if (!r.ok) {
           const e = await r.json().catch(() => ({}));
@@ -503,14 +503,15 @@
           continue;
         }
         const data = await r.json();
-        const runs = data.workflow_runs || [];
+        // The API documents no ordering guarantee for workflow runs, so sort 
newest-first ourselves.
+        const runs = (data.workflow_runs || []).sort((a, b) => new 
Date(b.created_at) - new Date(a.created_at));
         if (runs.length === 0) {
           const name = await fetchWorkflowName(file);
           results.push({ file, name, error: 'No runs found' });
           continue;
         }
         const latest = runs[0];
-        const name = await fetchWorkflowName(file) || latest.name;
+        const name = latest.name;
         results.push({
           file,
           name,
@@ -549,6 +550,12 @@
     return `${Math.floor(diff / 86400)}d ago`;
   }
 
+  const HTML_ESCAPES = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': 
'&quot;', "'": '&#39;' };
+
+  function esc(value) {
+    return String(value).replace(/[&<>"']/g, c => HTML_ESCAPES[c]);
+  }
+
   function renderCard(wf, idx) {
     const s = effectiveStatus(wf);
     const bars = (wf.history || []).slice(0, 10).map(h => {
@@ -559,13 +566,13 @@
       <div class="workflow-card" style="animation-delay:${idx * 0.06}s">
         <div class="status-dot ${s}"></div>
         <div class="workflow-info">
-          <div class="workflow-name">${wf.name || wf.file}</div>
+          <div class="workflow-name">${esc(wf.name || wf.file)}</div>
           <div class="workflow-sub">
-            <span>${wf.file}</span>
-            ${wf.head_branch  ? `<span>branch: ${wf.head_branch}</span>` : ''}
-            ${wf.run_number   ? `<a href="${wf.html_url}" target="_blank">run 
#${wf.run_number}</a>` : ''}
+            <span>${esc(wf.file)}</span>
+            ${wf.head_branch  ? `<span>branch: ${esc(wf.head_branch)}</span>` 
: ''}
+            ${wf.run_number   ? `<a href="${esc(wf.html_url)}" 
target="_blank">run #${wf.run_number}</a>` : ''}
             ${wf.updated_at   ? `<span>${timeAgo(wf.updated_at)}</span>` : ''}
-            ${wf.error        ? `<span class="err">⚠ ${wf.error}</span>` : ''}
+            ${wf.error        ? `<span class="err">⚠ ${esc(wf.error)}</span>` 
: ''}
           </div>
         </div>
         <div class="history-bars">${bars}</div>

Reply via email to