99 if : ${{ contains(github.event.discussion.category.name, 'Actions') }}
1010 runs-on : ubuntu-latest
1111 env :
12- GH_TOKEN : ${{ secrets.GITHUB_TOKEN }} # Global authentication for gh CLI
12+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
1313 steps :
1414 - name : Get discussion body html
1515 id : get_discussion_body_html
16- env :
16+ env :
1717 OWNER : ${{ github.repository_owner }}
1818 REPO : ${{ github.event.repository.name }}
1919 DISCUSSION_NUMBER : ${{ github.event.discussion.number }}
3030
3131 echo 'DISCUSSION_BODY_HTML='$(jq -r '.data.repository.discussion.bodyHTML' discussion_data.json) >> $GITHUB_ENV
3232 echo 'DISCUSSION_ID='$(jq -r '.data.repository.discussion.id' discussion_data.json) >> $GITHUB_ENV
33-
34- - run : npm install jsdom
3533
34+ - run : npm install jsdom dompurify
3635
3736 - name : Extract Title and Body Text
3837 id : extract_text
4847 const { DISCUSSION_BODY_HTML } = process.env;
4948 const fragment = JSDOM.fragment(DISCUSSION_BODY_HTML);
5049 let body = '';
51- // Find all <h3> and <p> pairs
5250 const h3s = Array.from(fragment.querySelectorAll('h3'));
5351 h3s.forEach(h3 => {
5452 const heading = h3.textContent.trim();
5957 body = p.textContent.trim();
6058 }
6159 });
62- // Remove leading/trailing quotes from body
6360 body = body.replace(/^['\"]+|['\"]+$/g, '');
6461 const title = process.env.DISCUSSION_TITLE || '';
6562 core.info(`Extracted title: ${title}`);
6966 - name : Extract Primary and Secondary Topic Areas
7067 id : extract_topics
7168 uses : actions/github-script@v6
69+ env :
70+ DISCUSSION_BODY_HTML : ${{ env.DISCUSSION_BODY_HTML }}
7271 with :
7372 result-encoding : string
7473 script : |
7877 const fragment = JSDOM.fragment(DISCUSSION_BODY_HTML);
7978 let primary = '';
8079 let secondary = '';
81- // Find all <h3> and <p> pairs (form headings as h3, answers as p)
8280 const h3s = Array.from(fragment.querySelectorAll('h3'));
8381 h3s.forEach(h3 => {
8482 const heading = h3.textContent.trim();
85- // Look for the next <p> sibling after each heading
8683 let p = h3.nextElementSibling;
8784 while (p && p.tagName !== 'P') p = p.nextElementSibling;
8885 if (!p) return;
@@ -100,78 +97,87 @@ jobs:
10097 - name : Auto-label by keyword search
10198 id : auto_label_keywords
10299 uses : actions/github-script@v6
100+ env :
101+ EXTRACT_TEXT_RESULT : ${{ steps.extract_text.outputs.result }}
103102 with :
104103 result-encoding : string
105104 script : |
106- // Keyphrase to label mapping
105+ const jsdom = require('jsdom');
106+ const { JSDOM } = jsdom;
107+ const createDOMPurify = require('dompurify');
108+ const window = (new JSDOM('')).window;
109+ const DOMPurify = createDOMPurify(window);
110+
107111 const labelMap = [
108112 {
109113 label: 'Workflow Deployment',
110114 keywords: [
111- "deployment error",
112- "publish artifact",
113- "release failure",
114- "deployment target",
115- "github pages",
116- "deployment issue",
117- "release workflow",
118- "target environment"
119- ]
115+ "deployment error",
116+ "publish artifact",
117+ "release failure",
118+ "deployment target",
119+ "github pages",
120+ "deployment issue",
121+ "release workflow",
122+ "target environment"
123+ ]
120124 },
121125 {
122126 label: 'Workflow Configuration',
123127 keywords: [
124- "yaml syntax",
125- "job dependency",
126- "setup error",
127- "workflow file",
128- "configuration issue",
129- "matrix strategy",
130- "define env",
131- "secret management",
132- "environment setup",
133- "config job"
134- ]
128+ "yaml syntax",
129+ "job dependency",
130+ "setup error",
131+ "workflow file",
132+ "configuration issue",
133+ "matrix strategy",
134+ "define env",
135+ "secret management",
136+ "environment setup",
137+ "config job"
138+ ]
135139 },
136140 {
137141 label: 'Schedule & Cron Jobs',
138142 keywords: [
139- "cron job",
140- "scheduled workflow",
141- "timing issue",
142- "delay trigger",
143- "timezone error",
144- "periodic run",
145- "recurring schedule",
146- "interval workflow",
147- "scheduled trigger",
148- "cron expression"
149- ]
143+ "cron job",
144+ "scheduled workflow",
145+ "timing issue",
146+ "delay trigger",
147+ "timezone error",
148+ "periodic run",
149+ "recurring schedule",
150+ "interval workflow",
151+ "scheduled trigger",
152+ "cron expression"
153+ ]
150154 },
151155 {
152156 label: 'Metrics & Insights',
153157 keywords: [
154- "usage metrics",
155- "performance trend",
156- "analytics graph",
157- "stats dashboard",
158- "timeseries graph",
159- "insight report",
160- "metric tracking",
161- "workflow analytics",
162- "performance metric",
163- "statistics report"
164- ]
158+ "usage metrics",
159+ "performance trend",
160+ "analytics graph",
161+ "stats dashboard",
162+ "timeseries graph",
163+ "insight report",
164+ "metric tracking",
165+ "workflow analytics",
166+ "performance metric",
167+ "statistics report"
168+ ]
165169 }
166170 ];
167171 const miscLabel = 'Misc';
168172 let title = '';
169173 let body = '';
170174 try {
171- const parsed = JSON.parse(`${{ steps.extract_text.outputs.result }}`);
172- title = parsed.title || '';
173- body = parsed.body || '';
174- } catch (e) {}
175+ const parsed = JSON.parse(process.env.EXTRACT_TEXT_RESULT);
176+ title = DOMPurify.sanitize(parsed.title || '', { ALLOWED_TAGS: [], ALLOWED_ATTR: [] }).trim();
177+ body = DOMPurify.sanitize(parsed.body || '', { ALLOWED_TAGS: [], ALLOWED_ATTR: [] }).trim();
178+ } catch (e) {
179+ core.error('Failed to parse or sanitize discussion text: ' + e.message);
180+ }
175181 const text = (title + ' ' + body).toLowerCase();
176182 let foundLabel = miscLabel;
177183 core.info(`Auto-label debug: text to match: '${text}'`);
@@ -188,6 +194,7 @@ jobs:
188194 }
189195 core.info(`Auto-label debug: selected label: '${foundLabel}'`);
190196 return foundLabel;
197+
191198 - name : Fetch label ID for primary topic
192199 id : fetch_primary_label_id
193200 env :
@@ -239,6 +246,7 @@ jobs:
239246
240247 SECONDARY_LABEL_ID=$(jq -r '.data.repository.labels.edges[0]?.node?.id // empty' secondary_label_data.json)
241248 echo "SECONDARY_LABEL_ID=$SECONDARY_LABEL_ID" >> $GITHUB_ENV
249+
242250 - name : Fetch label ID for auto-label
243251 id : fetch_auto_label_id
244252 env :
0 commit comments