getreadystack commited on
Commit
1610627
·
verified ·
1 Parent(s): 3c6a9c3

Upload index.html with huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +217 -17
index.html CHANGED
@@ -1,19 +1,219 @@
1
  <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  </html>
 
1
  <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <title>.NET EOL & CRA Support Window Gate — free online check</title>
7
+ <style>
8
+ :root{color-scheme:light}body{margin:0;font:15px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif;background:#faf9f5;color:#1a1512}
9
+ .wrap{max-width:960px;margin:0 auto;padding:28px 18px 60px}h1{font-size:26px;margin:0 0 6px}.lede{color:#4a403a;margin:0 0 16px}
10
+ textarea{width:100%;min-height:260px;font:13px/1.45 ui-monospace,Menlo,Consolas,monospace;padding:12px;border:1px solid #d9d0c4;border-radius:8px;box-sizing:border-box}
11
+ .bar{display:flex;gap:10px;align-items:center;margin:12px 0}button{font:600 15px system-ui;padding:10px 18px;border:0;border-radius:8px;background:#1a1512;color:#fff;cursor:pointer}
12
+ input[type=date]{font:14px system-ui;padding:8px;border:1px solid #d9d0c4;border-radius:6px}
13
+ table{border-collapse:collapse;width:100%;font-size:14px;margin-top:10px}th,td{text-align:left;padding:8px 10px;border-bottom:1px solid #eee;vertical-align:top}th{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:#7a6e65}
14
+ .sev.error{color:#a8551f;font-weight:700}.sev.warn,.sev.warning{color:#96691f}.ok{color:#2a6446;font-weight:600}.sum{font-weight:600}
15
+ .note{font-size:13px;color:#7a6e65;margin-top:18px}#paid{margin-top:22px;padding:14px 16px;border:1px solid #e2d8cb;border-radius:10px;background:#fffdfa}
16
+ </style>
17
+ <script data-grow-initializer="">!(function(){window.growMe||((window.growMe=function(e){window.growMe._.push(e);}),(window.growMe._=[]));var e=document.createElement("script");(e.type="text/javascript"),(e.src="https://faves.grow.me/main.js"),(e.defer=!0),e.setAttribute("data-grow-faves-site-id","U2l0ZTphZjljMzgyMC0yMmU5LTRhYjgtYTIxNS03OTRhMGY1N2ExYzM=");var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t);})();</script>
18
+ <!--yj-canon-s101--><link rel="canonical" href="https://getreadystack.com/tools/dotnet-eol-support-gate"></head>
19
+ <body>
20
+ <div class="wrap">
21
+ <h1>.NET EOL & CRA Support Window Gate</h1>
22
+ <p class="lede">Reads a .csproj or global.json and dates every target framework against Microsoft's published end-of-support day, then against the support period you promise buyers. Runs entirely in your browser — nothing is uploaded.</p>
23
+ <textarea id="src" spellcheck="false">&lt;Project Sdk="Microsoft.NET.Sdk"&gt;
24
+
25
+ &lt;PropertyGroup&gt;
26
+ &lt;TargetFrameworks&gt;net8.0;net9.0;net6.0&lt;/TargetFrameworks&gt;
27
+ &lt;Nullable&gt;enable&lt;/Nullable&gt;
28
+ &lt;NuGetAudit&gt;false&lt;/NuGetAudit&gt;
29
+ &lt;/PropertyGroup&gt;
30
+
31
+ &lt;ItemGroup&gt;
32
+ &lt;PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.36" /&gt;
33
+ &lt;PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.4" /&gt;
34
+ &lt;PackageReference Include="Xamarin.Essentials" Version="1.8.1" /&gt;
35
+ &lt;PackageReference Include="Serilog" Version="4.2.0" /&gt;
36
+ &lt;/ItemGroup&gt;
37
+
38
+ &lt;/Project&gt;</textarea>
39
+ <div class="bar"><button id="go">Check</button><label>as of <input type="date" id="today"></label><label>support period <select id="sy"><option>3</option><option selected>5</option><option>10</option></select> years</label></div>
40
+ <div id="out" aria-live="polite"></div>
41
+ <p id="next"><a href="https://buy.polar.sh/polar_cl_Z3kgDSemeuFusqV4CgHenwXtVCDgUEjo92a1x2d70T6" data-polar-checkout data-polar-checkout-theme="light">Sweep every project file in the workspace and write the dated support-window evidence file</a><br><small>$29 once &middot; one licence key per person or team seat &middot; 7-day full refund. Having a contractor inventory runtime support dates across a solution and write the evidence page is commonly quoted as a multi-day engagement.</small></p>
42
+ <div id="paid"><strong>Free here:</strong> Dates one .csproj or global.json you have open: every target framework, SDK pin and runtime-bound package gets its real end-of-support date, the days left, and the months it falls short of the support period you promise.. <strong>In the editor:</strong> Runs the same dating over every project file in the workspace at once and writes the dated support-window evidence file (Markdown + CSV) you keep and hand to an auditor. — that part asks for a licence key.</div>
43
+ <p class="note">Same engine as the VS Code extension, byte for byte.</p>
44
+ </div>
45
+ <script>var DOTNET_EOL_RULES = [
46
+ {"id": "tfm_out_of_support", "sev": "error", "title": "Target framework is past its end-of-support date", "why": "Microsoft ships no security fixes for it, so every later CVE in the runtime stays open in your build."},
47
+ {"id": "tfm_ends_soon", "sev": "error", "title": "Target framework loses support inside 180 days", "why": "A runtime bump is a release-planning item, not a patch; 180 days is the last comfortable window to schedule it."},
48
+ {"id": "support_window_gap", "sev": "error", "title": "Runtime dies before the support period you promise ends", "why": "CRA Article 13(8) makes the manufacturer support the product for the declared period, at least 5 years, so the runtime has to outlive the promise."},
49
+ {"id": "support_period_undeclared", "sev": "warn", "title": "No support period declared in the project file", "why": "CRA Article 13(8) requires a determined support period; if it is nowhere in the repo, the gate has to assume the 5-year floor."},
50
+ {"id": "framework_retired", "sev": "error", "title": ".NET Framework 4.6.1 or older", "why": ".NET Framework 4.5.2, 4.6 and 4.6.1 all left support on 2022-04-26 and never come back."},
51
+ {"id": "xamarin_retired", "sev": "error", "title": "Xamarin target or package reference", "why": "Xamarin support ended on 2024-05-01; the .NET MAUI move is the only supported path."},
52
+ {"id": "sdk_pin_out_of_support", "sev": "error", "title": "global.json pins an SDK band that is out of support", "why": "A pinned dead SDK silently overrides an up-to-date machine and rebuilds the dead runtime on every CI run."},
53
+ {"id": "preview_runtime", "sev": "error", "title": "Preview or release-candidate runtime in a shipping project", "why": "Preview builds carry no support period at all, so they cannot back any declared support promise."},
54
+ {"id": "runtime_bound_package", "sev": "error", "title": "Package pinned to a runtime version that is out of support", "why": "ASP.NET Core, EF Core and Microsoft.Extensions.* follow the .NET release lifecycle exactly, so a 6.x or 9.x pin is already unpatched."},
55
+ {"id": "nuget_audit_off", "sev": "error", "title": "NuGet vulnerability audit disabled or not covering transitive packages", "why": "CRA Annex I Part II asks manufacturers to handle vulnerabilities in the components they ship, including the ones they did not pick directly."}
56
+ ]
57
+ ;</script>
58
+ <script>// .NET EOL & CRA Support Window Gate — one brain, used by the extension and by the free web page.
59
+ 'use strict';
60
+ var RULES = (typeof module !== 'undefined') ? require('./rules.json') : window.DOTNET_EOL_RULES;
61
+
62
+ // Microsoft .NET release lifecycle — published end-of-support dates.
63
+ var TFM_EOL = {
64
+ 'netcoreapp3.1': '2022-12-13', 'net5.0': '2022-05-10', 'net6.0': '2024-11-12',
65
+ 'net7.0': '2024-05-14', 'net8.0': '2026-11-10', 'net9.0': '2026-05-12', 'net10.0': '2028-11-14'
66
+ };
67
+ var MAJOR_EOL = { '3': '2022-12-13', '5': '2022-05-10', '6': '2024-11-12', '7': '2024-05-14', '8': '2026-11-10', '9': '2026-05-12', '10': '2028-11-14' };
68
+ var FX_RETIRED = { 'net45': '2016-01-12', 'net451': '2016-01-12', 'net452': '2022-04-26', 'net46': '2022-04-26', 'net461': '2022-04-26' };
69
+ var XAMARIN_END = '2024-05-01';
70
+ var RUNTIME_PKG = /^(Microsoft\.AspNetCore\.|Microsoft\.EntityFrameworkCore|Microsoft\.Extensions\.)/;
71
+ var SOON_DAYS = 180;
72
+
73
+ function day(s) { var p = String(s || '').slice(0, 10).split('-'); return Date.UTC(+p[0], (+p[1] || 1) - 1, +p[2] || 1); }
74
+ function daysBetween(a, b) { return Math.round((day(b) - day(a)) / 86400000); }
75
+ function iso(ms) { return new Date(ms).toISOString().slice(0, 10); }
76
+ function addYears(s, n) { var d = new Date(day(s)); d.setUTCFullYear(d.getUTCFullYear() + Math.round(n)); return iso(d.getTime()); }
77
+ function months(a, b) { return Math.round(daysBetween(a, b) / 30.44); }
78
+
79
+ function check(text, opts) {
80
+ opts = opts || {};
81
+ var today = String(opts.today || new Date().toISOString().slice(0, 10)).slice(0, 10);
82
+ var years = Number(opts.support_years || 5) || 5;
83
+ var placed = String(opts.placed_on_market || today).slice(0, 10);
84
+ var lines = String(text == null ? '' : text).split(/\r?\n/);
85
+ var out = [];
86
+ var declared = null, declaredLine = 0, sawProject = false, sawTfm = false;
87
+ var auditOff = false, auditMode = false, sawAudit = false;
88
+
89
+ function add(id, line, msg) { out.push({ check: id, sev: (RULES.filter(function (r) { return r.id === id; })[0] || {}).sev || 'error', msg: msg, line: line }); }
90
+
91
+ // pass 1 — declared support period and audit settings
92
+ lines.forEach(function (raw, i) {
93
+ var n = i + 1;
94
+ var d = raw.match(/(?:SupportedUntil|SupportPeriodEnd|SupportEndDate|CRA-support-until)\s*[>:=]\s*"?(\d{4}-\d{2}-\d{2})/i);
95
+ if (d && !declared) { declared = d[1]; declaredLine = n; }
96
+ if (/<Project\b|"sdk"\s*:/i.test(raw)) sawProject = true;
97
+ if (/<NuGetAudit>\s*false\s*</i.test(raw)) { auditOff = true; add('nuget_audit_off', n, 'NuGetAudit is switched off, so no package in this project is checked against the advisory database at build time. CRA Annex I Part II expects shipped components to be screened.'); }
98
+ if (/<NuGetAudit>\s*true\s*</i.test(raw)) sawAudit = true;
99
+ if (/<NuGetAuditMode>\s*all\s*</i.test(raw)) { auditMode = true; sawAudit = true; }
100
+ });
101
+ if (sawProject && !auditOff && sawAudit && !auditMode) add('nuget_audit_off', 1, 'NuGetAuditMode is not set to "all", so only your direct PackageReferences are screened and transitive dependencies — where most shipped CVEs live — are skipped.');
102
+
103
+ var windowEnd = declared || addYears(placed, years);
104
+ if (sawProject && !declared) {
105
+ add('support_period_undeclared', 1, 'No support period is declared in this file, so the gate uses the CRA Article 13(8) floor: ' + years + ' years from ' + placed + ', ending ' + windowEnd + '. Add <SupportedUntil>' + windowEnd + '</SupportedUntil> to make the promise explicit.');
106
+ }
107
+
108
+ function dateTfm(tfm, n) {
109
+ var t = tfm.toLowerCase().trim();
110
+ if (/-(preview|rc)/.test(t) || /^net(1[1-9])\.0/.test(t)) {
111
+ add('preview_runtime', n, tfm + ' is a preview or unreleased runtime. Preview builds carry no support period at all, so nothing you promise a buyer is backed by a patch stream.');
112
+ return;
113
+ }
114
+ if (/^(monoandroid|xamarin|xamarinios|xamarinmac)/.test(t)) {
115
+ add('xamarin_retired', n, tfm + ' is a Xamarin target framework. Xamarin support ended on ' + XAMARIN_END + ', ' + daysBetween(XAMARIN_END, today) + ' days ago; move the head project to a net10.0-android / net10.0-ios .NET MAUI target.');
116
+ return;
117
+ }
118
+ if (FX_RETIRED[t]) {
119
+ add('framework_retired', n, tfm + ' (.NET Framework) left support on ' + FX_RETIRED[t] + ', ' + daysBetween(FX_RETIRED[t], today) + ' days ago. Retarget to net472 or later, or to net10.0.');
120
+ return;
121
+ }
122
+ var base = t.split('-')[0];
123
+ var eol = TFM_EOL[base];
124
+ if (!eol) return;
125
+ var left = daysBetween(today, eol);
126
+ if (left < 0) {
127
+ add('tfm_out_of_support', n, tfm + ' lost security servicing on ' + eol + ' — ' + (-left) + ' days ago. Every runtime CVE published since that day is still open in this build.');
128
+ } else if (left <= SOON_DAYS) {
129
+ add('tfm_ends_soon', n, tfm + ' stops receiving security fixes on ' + eol + ', in ' + left + ' days. A runtime bump is a release, not a patch — schedule it now.');
130
+ }
131
+ if (left >= 0 && day(eol) < day(windowEnd)) {
132
+ var tail = (day(TFM_EOL['net10.0']) >= day(windowEnd)) ? ' net10.0 covers that promise to ' + TFM_EOL['net10.0'] + '.' : ' Even net10.0 only reaches ' + TFM_EOL['net10.0'] + ', so this promise needs a planned retarget on the roadmap, not a one-off bump.';
133
+ add('support_window_gap', n, tfm + ' dies on ' + eol + ', but the support period running to ' + windowEnd + ' leaves ' + months(eol, windowEnd) + ' months you have promised to patch on a runtime nobody patches.' + tail);
134
+ }
135
+ }
136
+
137
+ // pass 2 — target frameworks, SDK pins, packages
138
+ lines.forEach(function (raw, i) {
139
+ var n = i + 1, m;
140
+ if ((m = raw.match(/<TargetFramework>\s*([^<\s]+)\s*</i))) { sawTfm = true; dateTfm(m[1], n); }
141
+ if ((m = raw.match(/<TargetFrameworks>\s*([^<]+)</i))) {
142
+ sawTfm = true;
143
+ m[1].split(';').forEach(function (t) { if (t.trim()) dateTfm(t, n); });
144
+ }
145
+ if ((m = raw.match(/"version"\s*:\s*"(\d+)\.(\d+)\.(\d+)/))) {
146
+ var eol = MAJOR_EOL[m[1]];
147
+ if (eol && daysBetween(today, eol) < 0) {
148
+ add('sdk_pin_out_of_support', n, 'global.json pins SDK ' + m[1] + '.' + m[2] + '.' + m[3] + '. That band went out of support on ' + eol + ', ' + daysBetween(eol, today) + ' days ago, and the pin overrides every up-to-date SDK on the build machine.');
149
+ } else if (eol && daysBetween(today, eol) <= SOON_DAYS) {
150
+ add('sdk_pin_out_of_support', n, 'global.json pins SDK ' + m[1] + '.' + m[2] + '.' + m[3] + ', whose band goes out of support on ' + eol + ', in ' + daysBetween(today, eol) + ' days. Every CI run will keep building on it until the pin moves.');
151
+ }
152
+ }
153
+ if ((m = raw.match(/<PackageReference\s+Include="([^"]+)"[^>]*Version="([^"]+)"/i))) {
154
+ var id = m[1], ver = m[2];
155
+ if (/^Xamarin\./i.test(id)) {
156
+ add('xamarin_retired', n, id + ' ' + ver + ' is a Xamarin package. Xamarin support ended on ' + XAMARIN_END + ', ' + daysBetween(XAMARIN_END, today) + ' days ago, and no fix is coming for anything found in it.');
157
+ } else if (RUNTIME_PKG.test(id)) {
158
+ var maj = (ver.match(/^(\d+)/) || [])[1];
159
+ var pe = MAJOR_EOL[maj];
160
+ if (pe && daysBetween(today, pe) < 0) {
161
+ add('runtime_bound_package', n, id + ' ' + ver + ' follows the .NET ' + maj + ' lifecycle, which ended on ' + pe + ' — ' + daysBetween(pe, today) + ' days ago. This package line is unpatched.');
162
+ }
163
+ }
164
+ }
165
+ });
166
+
167
+ return { findings: out, today: today, window_end: windowEnd, support_years: years, declared: !!declared, declared_line: declaredLine, rules: RULES.length, targets_seen: sawTfm };
168
+ }
169
+
170
+ var API = { engine: { check: check }, RULES: RULES, RULE_COUNT: RULES.length, TFM_EOL: TFM_EOL };
171
+ if (typeof module !== 'undefined') module.exports = API;
172
+ if (typeof window !== 'undefined') window.DOTNET_EOL_ENGINE = API;
173
+ </script>
174
+ <script>// ⛔뼈대 — kit/scaffold.py 가 찍는다 (s143). findings [{check,sev,msg,line?}] → 글/HTML. 같은 파일이 Node 와 브라우저에서 산다.
175
+ 'use strict';
176
+ function escapeHtml(s) { return String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;'); }
177
+ function summaryLine(res) {
178
+ const f = (res && res.findings) || []; const e = f.filter(function (x) { return String(x.sev || 'error').toLowerCase().startsWith('err'); }).length;
179
+ return f.length ? (f.length + ' finding' + (f.length === 1 ? '' : 's') + ' (' + e + ' error' + (e === 1 ? '' : 's') + ')') : 'Clean — no findings.';
180
+ }
181
+ function toText(res, label) {
182
+ const f = (res && res.findings) || [];
183
+ const lines = ['== ' + (label || '') + ' — ' + summaryLine(res)];
184
+ f.forEach(function (x) { lines.push(' ' + String(x.sev || 'error').toUpperCase().slice(0, 4) + ' ' + (x.line ? 'L' + x.line + ' ' : '') + (x.check || x.id || '') + ' ' + (x.msg || x.message || '')); });
185
+ return lines.join('\n');
186
+ }
187
+ function toHtml(res) {
188
+ const f = (res && res.findings) || [];
189
+ if (!f.length) return '<p class="ok">' + escapeHtml(summaryLine(res)) + '</p>';
190
+ const rows = f.map(function (x) { return '<tr><td class="sev ' + escapeHtml(String(x.sev || 'error')) + '">' + escapeHtml(String(x.sev || 'error')) + '</td><td>' + (x.line ? 'L' + escapeHtml(x.line) : '') + '</td><td><code>' + escapeHtml(x.check || x.id || '') + '</code></td><td>' + escapeHtml(x.msg || x.message || '') + '</td></tr>'; }).join('');
191
+ return '<p class="sum">' + escapeHtml(summaryLine(res)) + '</p><table><thead><tr><th>Level</th><th>Line</th><th>Check</th><th>What</th></tr></thead><tbody>' + rows + '</tbody></table>';
192
+ }
193
+ var DOTNET_EOL_REPORT = { escapeHtml: escapeHtml, summaryLine: summaryLine, toText: toText, toHtml: toHtml };
194
+ if (typeof module !== 'undefined' && module.exports) module.exports = DOTNET_EOL_REPORT;
195
+ else window.DOTNET_EOL_REPORT = DOTNET_EOL_REPORT;
196
+ </script>
197
+ <script>
198
+ (function(){
199
+ var src=document.getElementById('src'), out=document.getElementById('out'), go=document.getElementById('go'), today=document.getElementById('today');
200
+ today.value=new Date().toISOString().slice(0,10);
201
+ function run(){ try { var res=window.DOTNET_EOL_ENGINE.engine.check(src.value,{today:today.value,support_years:Number(document.getElementById('sy').value)}); out.innerHTML=window.DOTNET_EOL_REPORT.toHtml(res); } catch(e){ out.innerHTML='<p class="sev error">Could not run: '+String(e.message||e)+'</p>'; } }
202
+ go.onclick=run; if (src.value.trim()) run();
203
+ })();
204
+ </script>
205
+ <script defer data-auto-init src="https://cdn.jsdelivr.net/npm/@polar-sh/checkout@latest/dist/embed.global.js"></script>
206
+
207
+
208
+ <section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://www.npmjs.com/package/@readystack/dotnet-eol-support-gate" rel="noopener" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">npm</a><a href="https://www.npmjs.com/package/@readystack/dotnet-eol-support-gate" rel="noopener" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">MCP server (npx)</a><a href="https://hub.docker.com/r/getreadystack/dotnet-eol-support-gate" rel="noopener" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Docker Hub</a><pre style="margin:.5rem 0 0;padding:.6rem .8rem;background:#f3f5f8;border-radius:8px;font-size:.9rem;overflow:auto">npx @readystack/dotnet-eol-support-gate &lt;file&gt;
209
+ npx @readystack/dotnet-eol-support-gate --mcp
210
+ docker run --rm -v &quot;$PWD:/w&quot; getreadystack/dotnet-eol-support-gate /w</pre><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://buy.polar.sh/polar_cl_l6iN1uWt0FwWu7tBsczD0jWpP2vxFM54Wdwqb3KPi1G" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section>
211
+ <section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get one email when this rule changes</b><div style="opacity:.78;margin:.35rem 0 .8rem">We watch the regulation and vendor sources behind .NET EOL &amp; CRA Support Window Gate every day. When a rule changes, you get a single email with what changed and the updated check. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Notify me</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">You're on the list. One email when it changes.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"dotnet-eol-support-gate",lang:"en",source:"rule_alert"})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script></section>
212
+
213
+ <section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the complete version <span class="yj-price">$29</span></h2><p style="margin:.25rem 0 1rem;color:#41505f">This page is the working piece. The full pack has everything below.</p><p style="margin:0 0 1rem;color:#41505f">Dates every target framework in a .csproj against Microsoft's end-of-support day and against the support period you promise</p><p style="margin:0 0 1rem;color:#5b6b7c;font-size:.94rem">Having a contractor inventory runtime support dates across a solution and write the evidence page is commonly quoted as a multi-day engagement</p><a href="https://buy.polar.sh/polar_cl_Z3kgDSemeuFusqV4CgHenwXtVCDgUEjo92a1x2d70T6" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $29</a></section>
214
+ <section data-yj="yj-facts-v1" style="max-width:920px;margin:48px auto 0;padding:0 20px;font-family:system-ui,sans-serif"><style>.yj-article p{font:400 15.5px/1.7 system-ui,sans-serif;color:#26313b;margin:0 0 12px;max-width:72ch}.yj-article h2,.yj-article h3,.yj-article h4{font:600 17px/1.35 system-ui,sans-serif;color:#1b1f24;margin:22px 0 8px}.yj-article table{border-collapse:collapse;margin:10px 0 16px;font:14px/1.45 system-ui,sans-serif}.yj-article th,.yj-article td{border:1px solid #e2e6ea;padding:6px 10px;text-align:left}.yj-article th{background:#f4f6f8}.yj-article pre{background:#0f1720;color:#e6edf3;padding:12px 14px;border-radius:8px;overflow:auto;font:13px/1.5 ui-monospace,Menlo,monospace}.yj-article code{font:13px ui-monospace,Menlo,monospace;background:#f1f3f5;padding:1px 4px;border-radius:3px}.yj-article pre code{background:none;padding:0}.yj-article ul,.yj-article ol{font:15px/1.6 system-ui,sans-serif;color:#26313b}</style><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:34px 0 6px;color:#1b1f24">Questions people ask</h2><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does the .NET EOL &amp; CRA Support Window Gate actually do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It reads a .csproj, Directory.Build.props or global.json and dates it twice. First against the .NET release lifecycle: net9.0 ended on 2026-05-12, net8.0 ends on 2026-11-10, net10.0 runs to 2028-11-14. Then against the support period you promise buyers, reporting the months of promised patching that land after your runtime stops being patched.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Who is this for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Maintainers of .NET and .NET MAUI products sold into the EU, who under the Cyber Resilience Act must determine a support period of at least 5 years and hold it. Also anyone inheriting a solution and needing to know, by date, which of its projects are already unpatched before quoting the upgrade.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why not just ask a chatbot which .NET versions are supported?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Lifecycle dates move and models answer from a training snapshot, confidently naming the wrong month. This gate carries the dates as data and does the subtraction against your file and today&#x27;s date, so the answer is a day count you can put in a ticket rather than a sentence you have to verify.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What is free and what needs a licence key?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Dating the file you have open is free forever and needs no key: all 10 checks, every date, every day count. The licensed part is a different job - sweeping every project file in the workspace at once and writing the dated support-window evidence file you keep for an audit.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What would this cost done by a person?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Inventorying target frameworks, SDK pins and runtime-bound packages across a real solution, dating each against the lifecycle, and writing the evidence page is commonly quoted as a multi-day consulting engagement. The gate is $29 once, with a 7-day full refund, and the free tier already answers the single-file question.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does the .NET EOL & CRA Support Window Gate actually do?", "acceptedAnswer": {"@type": "Answer", "text": "It reads a .csproj, Directory.Build.props or global.json and dates it twice. First against the .NET release lifecycle: net9.0 ended on 2026-05-12, net8.0 ends on 2026-11-10, net10.0 runs to 2028-11-14. Then against the support period you promise buyers, reporting the months of promised patching that land after your runtime stops being patched."}}, {"@type": "Question", "name": "Who is this for?", "acceptedAnswer": {"@type": "Answer", "text": "Maintainers of .NET and .NET MAUI products sold into the EU, who under the Cyber Resilience Act must determine a support period of at least 5 years and hold it. Also anyone inheriting a solution and needing to know, by date, which of its projects are already unpatched before quoting the upgrade."}}, {"@type": "Question", "name": "Why not just ask a chatbot which .NET versions are supported?", "acceptedAnswer": {"@type": "Answer", "text": "Lifecycle dates move and models answer from a training snapshot, confidently naming the wrong month. This gate carries the dates as data and does the subtraction against your file and today's date, so the answer is a day count you can put in a ticket rather than a sentence you have to verify."}}, {"@type": "Question", "name": "What is free and what needs a licence key?", "acceptedAnswer": {"@type": "Answer", "text": "Dating the file you have open is free forever and needs no key: all 10 checks, every date, every day count. The licensed part is a different job - sweeping every project file in the workspace at once and writing the dated support-window evidence file you keep for an audit."}}, {"@type": "Question", "name": "What would this cost done by a person?", "acceptedAnswer": {"@type": "Answer", "text": "Inventorying target frameworks, SDK pins and runtime-bound packages across a real solution, dating each against the lifecycle, and writing the evidence page is commonly quoted as a multi-day consulting engagement. The gate is $29 once, with a 7-day full refund, and the free tier already answers the single-file question."}}]}</script><div id="yj-ask" style="margin:36px 0 0;padding:18px 18px 16px;border:1px solid #e2e6ea;border-radius:10px;background:#fbfbfc"><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:0 0 4px;color:#1b1f24">Ask about this tool</h2><p style="font:400 13.5px/1.5 system-ui,sans-serif;margin:0 0 12px;color:#5b6570">One question, answered by the person who built it. Your email only if you want the answer sent.</p><form onsubmit="return yjAsk(event)" style="display:grid;gap:8px"><textarea name="question" required minlength="10" maxlength="2000" rows="3" placeholder="Your question" style="font:14px/1.5 system-ui,sans-serif;padding:10px;border:1px solid #cfd6dd;border-radius:6px"></textarea><div style="display:flex;gap:8px;flex-wrap:wrap"><input type="email" name="email" placeholder="Email (optional)" style="flex:1;min-width:200px;font:14px system-ui,sans-serif;padding:9px 10px;border:1px solid #cfd6dd;border-radius:6px"><button type="submit" style="font:600 14px system-ui,sans-serif;padding:9px 18px;border:0;border-radius:6px;background:#1b1f24;color:#fff;cursor:pointer">Send</button></div><p id="yj-ask-msg" style="font:13px system-ui,sans-serif;margin:2px 0 0;color:#0a6238;min-height:1em"></p></form><script>function yjAsk(e){e.preventDefault();var f=e.target,m=document.getElementById("yj-ask-msg");m.style.color="#0a6238";fetch(location.origin+"/api/ask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sku:0,slug:"dotnet-eol-support-gate",lang:"en",question:f.question.value,email:f.email.value,page:location.pathname})}).then(function(r){if(!r.ok)throw 0;m.textContent="Thanks \u2014 noted. Answers land on this page and, if you left an email, in your inbox.";f.question.value="";}).catch(function(){m.style.color="#a8551f";m.textContent="Could not send. Please try again in a minute.";});return false;}</script></div></section><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Same job, next tool</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Free in the browser. The full version is a one-time licence, 7-day refund.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/base-image-eol-lint"><span class="yj-rel-t">Base Image EOL Lint - Dockerfile and CI end-of-life check</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/actions-deprecation-lint-2026"><span class="yj-rel-t">GitHub Actions Deprecation Lint: the 2026 runner and action EOL dates</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/personal-data-map-dsar-audit"><span class="yj-rel-t">Personal Data Map: find every column a GDPR subject access request has to reac</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/cra-24-72-14-reporting-lint"><span class="yj-rel-t">CRA 24/72/14 Reporting Lint (Article 14)</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/cra-readiness-audit"><span class="yj-rel-t">CRA Readiness Audit for EU Cyber Resilience Act</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/csaf-advisory-check-cra-2026"><span class="yj-rel-t">CSAF Advisory Check for CRA 2026</span><span class="yj-rel-p">$29</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section><!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script><div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div>
215
+ <script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||'')})}).catch(function(){});}catch(e){}})();</script>
216
+
217
+ <script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use'})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script>
218
+ </body>
219
  </html>