<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Abel Solutions</title>
    <description>The latest articles on DEV Community by Abel Solutions (@abel-dev).</description>
    <link>https://dev.to/abel-dev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4045531%2F69d74608-9a17-4aef-9b3e-86c17686ca42.png</url>
      <title>DEV Community: Abel Solutions</title>
      <link>https://dev.to/abel-dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abel-dev"/>
    <language>en</language>
    <item>
      <title>Your cron job fires on the wrong day</title>
      <dc:creator>Abel Solutions</dc:creator>
      <pubDate>Tue, 28 Jul 2026 14:00:00 +0000</pubDate>
      <link>https://dev.to/abel-dev/your-cron-job-fires-on-the-wrong-day-5a71</link>
      <guid>https://dev.to/abel-dev/your-cron-job-fires-on-the-wrong-day-5a71</guid>
      <description>&lt;p&gt;A team ports a job from a Quartz scheduler to a Unix crontab. The Quartz trigger ran Monday through Friday — day-of-week field &lt;code&gt;2-6&lt;/code&gt;, because Quartz numbers Sunday as &lt;code&gt;1&lt;/code&gt;, so Monday is &lt;code&gt;2&lt;/code&gt; and Friday is &lt;code&gt;6&lt;/code&gt;. Someone copies that same numeric range, &lt;code&gt;2-6&lt;/code&gt;, straight into the new Unix crontab entry. Nobody touches the logic. Nobody gets an error. The cron daemon accepts the expression without complaint, because &lt;code&gt;2-6&lt;/code&gt; is a perfectly valid day-of-week range in Unix cron too.&lt;/p&gt;

&lt;p&gt;It just means something else there. Unix cron numbers Sunday as &lt;code&gt;0&lt;/code&gt;, so &lt;code&gt;2-6&lt;/code&gt; is Tuesday through Saturday. The job now skips Monday and runs an extra day on Saturday — and because nothing crashes, nobody notices until someone asks why a report that's supposed to close out the work week is missing Monday's numbers and showing up on the weekend. That's the incident shape: not a crash, not a stack trace, just a schedule that's quietly wrong until a human cross-checks it against a calendar.&lt;/p&gt;

&lt;p&gt;It's a symptom of a bigger problem: there is no single "cron standard." There are roughly eight dialects in common production use, and they agree on the broad strokes — five-ish fields, &lt;code&gt;*&lt;/code&gt;/&lt;code&gt;,&lt;/code&gt;/&lt;code&gt;-&lt;/code&gt;/&lt;code&gt;/&lt;/code&gt; operators — while disagreeing on the details that actually matter when you port an expression between systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why: eight dialects, one shared ancestor
&lt;/h2&gt;

&lt;p&gt;The lineage is short. POSIX/Unix cron (Version 7 Unix, 1970s) is the conservative baseline: five fields, no step syntax, no aliases. Paul Vixie's 1987 cron — what most Linux distributions actually ship — is the superset everyone means when they say "cron": it adds &lt;code&gt;*/n&lt;/code&gt; steps, named days and months, and &lt;code&gt;@daily&lt;/code&gt;-style aliases. From there the tree splits two ways. Some systems fork Vixie's syntax and quietly change a rule or two: GitHub Actions and Kubernetes CronJobs both use 5-field, Vixie-style parsing, but they diverge from Vixie — and from each other — on aliases. GitHub Actions rejects &lt;code&gt;@&lt;/code&gt;-aliases outright; Kubernetes keeps them and adds &lt;code&gt;@every&lt;/code&gt; on top. Neither accepts &lt;code&gt;7&lt;/code&gt; as an alias for Sunday, though, even though Vixie cron does. Other systems reinvent the field layout from scratch: Quartz (Java) adds a seconds field and flips day-of-week numbering; Spring's &lt;code&gt;@Scheduled&lt;/code&gt; borrows Quartz's advanced tokens but keeps Unix-style day-of-week numbering; AWS EventBridge adds a year field and borrows Quartz's &lt;code&gt;?&lt;/code&gt;/&lt;code&gt;L&lt;/code&gt;/&lt;code&gt;W&lt;/code&gt;/&lt;code&gt;#&lt;/code&gt; tokens along with its Sunday-is-1 numbering.&lt;/p&gt;

&lt;p&gt;Here's the full comparison, built directly from CronPro's dialect definitions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dialect&lt;/th&gt;
&lt;th&gt;Fields&lt;/th&gt;
&lt;th&gt;Sunday =&lt;/th&gt;
&lt;th&gt;Aliases (&lt;code&gt;@daily&lt;/code&gt;, etc.)&lt;/th&gt;
&lt;th&gt;DOM/DOW "don't care"&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Unix / POSIX&lt;/td&gt;
&lt;td&gt;5: min hour dom mon dow&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0&lt;/code&gt; (no &lt;code&gt;7&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;None (strict POSIX)&lt;/td&gt;
&lt;td&gt;No &lt;code&gt;?&lt;/code&gt; token — ORs DOM and DOW when both are restricted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vixie (Linux default)&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0&lt;/code&gt; (&lt;code&gt;7&lt;/code&gt; also accepted)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@hourly&lt;/code&gt; … &lt;code&gt;@reboot&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No &lt;code&gt;?&lt;/code&gt; token — same OR rule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GitHub Actions&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0&lt;/code&gt; (&lt;code&gt;7&lt;/code&gt; not documented)&lt;/td&gt;
&lt;td&gt;None — explicitly rejected&lt;/td&gt;
&lt;td&gt;No &lt;code&gt;?&lt;/code&gt; token — ORs DOM and DOW when both are restricted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kubernetes CronJob&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0&lt;/code&gt; (&lt;code&gt;7&lt;/code&gt; rejected by the API server)&lt;/td&gt;
&lt;td&gt;Yes (&lt;code&gt;@hourly&lt;/code&gt; … &lt;code&gt;@every&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;No &lt;code&gt;?&lt;/code&gt; token — OR-behavior not stated explicitly for K8s in source&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS EventBridge&lt;/td&gt;
&lt;td&gt;6 (adds year)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;?&lt;/code&gt; required in exactly one of DOM/DOW&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quartz&lt;/td&gt;
&lt;td&gt;6–7 (adds seconds, optional year)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;?&lt;/code&gt; required in exactly one of DOM/DOW&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Spring &lt;code&gt;@Scheduled&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;6 (adds seconds)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0&lt;/code&gt; or &lt;code&gt;7&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Yes (since Spring 5.3)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;?&lt;/code&gt; accepted (5.3+), but not required in either field — semantics differ from Quartz/EventBridge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure Functions (NCRONTAB)&lt;/td&gt;
&lt;td&gt;6 (adds seconds)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;0&lt;/code&gt; (no &lt;code&gt;7&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;No &lt;code&gt;?&lt;/code&gt; token in this dialect — use &lt;code&gt;*&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Five traps that don't throw errors
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Sunday's number changes between dialects.&lt;/strong&gt; Unix, Vixie, and GitHub Actions number Sunday &lt;code&gt;0&lt;/code&gt;. Quartz and AWS EventBridge number it &lt;code&gt;1&lt;/code&gt;. That one-off shift is exactly what broke the crontab in the cold open: a Quartz &lt;code&gt;2-6&lt;/code&gt; (Mon–Fri) becomes Unix &lt;code&gt;2-6&lt;/code&gt; (Tue–Sat) if you copy the digits instead of re-deriving them. The fix is mechanical, not clever — re-map every numeric day value when you change dialects, or write the names (&lt;code&gt;MON-FRI&lt;/code&gt;) instead of numbers, since day names mean the same weekday in every dialect that supports them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Day-of-month and day-of-week are OR'd, not AND'd, when both are restricted.&lt;/strong&gt; &lt;code&gt;0 0 1-7 * 1&lt;/code&gt; looks like "the first Monday of the month." It isn't. When both fields carry a real restriction, Vixie-style cron fires the job if &lt;em&gt;either&lt;/em&gt; matches — so that expression runs on every day 1 through 7 of the month, &lt;em&gt;and&lt;/em&gt; on every Monday, whichever comes first. That's roughly 124 firings a year, not 12 — 84 from the date range plus 52 Mondays, minus the 12 days a year that are both (every month's first seven days contain exactly one Monday). This rule dates back to POSIX and holds in Vixie cron and the parsers derived from it — GitHub Actions documents the same behavior for &lt;code&gt;schedule:&lt;/code&gt; triggers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Kubernetes rejects &lt;code&gt;7&lt;/code&gt; as an alias for Sunday.&lt;/strong&gt; &lt;code&gt;0 0 * * 7&lt;/code&gt; is valid in a Vixie crontab. Point the same string at a Kubernetes CronJob and the API server rejects it outright: &lt;code&gt;end of range (7) above maximum (6)&lt;/code&gt;. Same Vixie-style day-of-week field, but Kubernetes's parser drops the &lt;code&gt;0&lt;/code&gt;/&lt;code&gt;7&lt;/code&gt; dual-alias that Vixie cron allows. Use &lt;code&gt;0 0 * * 0&lt;/code&gt; or &lt;code&gt;0 0 * * SUN&lt;/code&gt; instead — both work identically in both places.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. In AWS EventBridge, the sixth field is year, not seconds.&lt;/strong&gt; &lt;code&gt;cron(0 12 * * ? *)&lt;/code&gt; runs daily at noon UTC — six fields, but the extra one past Unix's five is &lt;code&gt;year&lt;/code&gt;, not sub-minute resolution. EventBridge has no seconds field at all. The other surprise in that same expression: the &lt;code&gt;?&lt;/code&gt; isn't decorative. EventBridge requires exactly one of day-of-month or day-of-week to be &lt;code&gt;?&lt;/code&gt;; &lt;code&gt;cron(* * * * * *)&lt;/code&gt; is invalid because neither field opts out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. In Quartz, &lt;code&gt;L&lt;/code&gt; in the day-of-week field means Saturday — literally the last day of the week — not "last occurrence in the month."&lt;/strong&gt; It's an easy substitution to get backwards under a deadline: &lt;code&gt;0 0 0 ? * L&lt;/code&gt; fires every Saturday at midnight, full stop. For "last Friday of the month," the token you actually want is &lt;code&gt;6L&lt;/code&gt; (day 6, last occurrence), not &lt;code&gt;L&lt;/code&gt; by itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;For plain 5-field cron with no nth-weekday token, don't try to force the constraint into one expression. Schedule wide, then filter in the job itself: &lt;code&gt;0 0 1-7 * *&lt;/code&gt; fires on every one of the month's first seven days, and the job's first step exits unless it's actually the day you want — &lt;code&gt;[ "$(date +%u)" = "1" ] || exit 0&lt;/code&gt; for Monday. Two steps beat one clever, wrong one-liner, and the guard is portable to any shell the job already runs in.&lt;/p&gt;

&lt;p&gt;Where the dialect gives you a native token, skip the workaround. Quartz and AWS EventBridge both support &lt;code&gt;#&lt;/code&gt; for "nth weekday of the month" directly: &lt;code&gt;0 0 0 ? * 2#1&lt;/code&gt; (Quartz) and &lt;code&gt;cron(0 0 ? * 2#1 *)&lt;/code&gt; (EventBridge) both mean, literally, "the first Monday" — no guard clause, no wide schedule, no OR-semantics trap to fall into in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this leaves you
&lt;/h2&gt;

&lt;p&gt;None of this is a defect in any one scheduler — each dialect is internally consistent, and the traps only show up at the seams, when an expression crosses from one system to another. We built a converter that handles the renumbering and de-stepping across all eight dialects and flags when a conversion is lossy — when the target dialect can't express what the source one did — instead of silently producing something that parses but means something different: &lt;a href="https://cronpro.dev" rel="noopener noreferrer"&gt;cronpro.dev&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cron</category>
      <category>devops</category>
      <category>kubernetes</category>
      <category>aws</category>
    </item>
    <item>
      <title>The NuGet gap Shai-Hulud exposed — and what we built to close part of it)</title>
      <dc:creator>Abel Solutions</dc:creator>
      <pubDate>Fri, 24 Jul 2026 13:44:04 +0000</pubDate>
      <link>https://dev.to/abel-dev/the-nuget-gap-shai-hulud-exposed-and-what-we-built-to-close-part-of-it-3k2j</link>
      <guid>https://dev.to/abel-dev/the-nuget-gap-shai-hulud-exposed-and-what-we-built-to-close-part-of-it-3k2j</guid>
      <description>&lt;p&gt;In September 2025, security researchers disclosed Shai-Hulud: a self-propagating worm that spread through the npm ecosystem by stealing maintainer credentials and using them to publish malicious versions of legitimate packages. CISA issued an alert within the week. By the time the dust settled, reporting put the number of poisoned packages above 500, including widely used libraries like &lt;code&gt;@ctrl/tinycolor&lt;/code&gt;. The mechanism was the interesting part: compromised packages scanned the developer or CI environment for npm tokens, GitHub personal access tokens, and cloud credentials, then used whatever they found to publish more malicious versions — automated propagation, not a single actor manually re-uploading. A second wave, reported in late November 2025, showed the technique hadn't gone away.&lt;/p&gt;

&lt;p&gt;None of that is .NET's problem, directly. But it raised an honest question for anyone shipping C# for a living: if the equivalent happened to a popular NuGet package tomorrow — a maintainer's account compromised, a malicious version pushed under a trusted name — what, exactly, would stop it from landing in your build the same day?&lt;/p&gt;

&lt;h2&gt;
  
  
  What NuGet actually has
&lt;/h2&gt;

&lt;p&gt;To answer that fairly, it's worth listing what already exists, because it's more than "nothing."&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Package signing.&lt;/strong&gt; Authors can sign packages, and every package accepted onto nuget.org is automatically repository-signed. Verification is available via &lt;code&gt;dotnet nuget verify&lt;/code&gt;, and starting with the .NET 8 SDK, signature verification is enabled by default using a certificate trust list Microsoft ships with the SDK. This establishes &lt;em&gt;provenance and integrity&lt;/em&gt; — the package you're restoring is the bit-for-bit artifact that account uploaded, unmodified in transit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Package ID prefix reservation.&lt;/strong&gt; Organizations can reserve an ID prefix after a domain-verification process; once reserved, nuget.org rejects any new package under that prefix from a different account. This is a real defense against someone else publishing packages under your reserved namespace — an impersonation control, not an anti-typosquatting one (it doesn't stop a similarly-named-but-different prefix like &lt;code&gt;Newtonsoft.Jso&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trusted Publishing (OIDC).&lt;/strong&gt; For CI-driven releases, nuget.org now supports exchanging a short-lived GitHub Actions OIDC token for a single-use, ~1-hour NuGet API key — removing the need for long-lived publish secrets sitting in CI, which is exactly the kind of credential Shai-Hulud went looking for on the npm side.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of that is real, and none of it is nothing. It's also all aimed at the same layer: proving &lt;em&gt;who published this&lt;/em&gt; and &lt;em&gt;that it wasn't tampered with in transit&lt;/em&gt;. Signing answers "is this the artifact that account uploaded." It does not answer "should I trust code from a brand-new version of this package the moment it lands."&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap: nothing gates on trust, and nothing gates on time
&lt;/h2&gt;

&lt;p&gt;Two things are true at once, and neither is a NuGet-specific failing — npm has the equivalent gaps, just a different attack surface (install-time scripts by default, versus automatic build-time hooks for .NET). The ecosystems evolved different defenses on different timelines.&lt;/p&gt;

&lt;p&gt;First: a signed, ID-verified package can still be &lt;em&gt;malicious&lt;/em&gt;. Signing proves identity and integrity, not intent. If an attacker compromises the account that owns a reserved prefix, or if a malicious actor legitimately owns the identity they're publishing under, every defense above passes cleanly.&lt;/p&gt;

&lt;p&gt;Second — and this is the part with a documented track record in .NET specifically — NuGet packages can carry executable logic that runs during &lt;strong&gt;build&lt;/strong&gt;, not just at runtime. MSBuild supports inline tasks and &lt;code&gt;.targets&lt;/code&gt;/&lt;code&gt;.props&lt;/code&gt; files shipped inside a package's &lt;code&gt;build/&lt;/code&gt; folder. For &lt;code&gt;PackageReference&lt;/code&gt; projects, those files get referenced through generated &lt;code&gt;.nuget.g.targets&lt;/code&gt;/&lt;code&gt;.props&lt;/code&gt; during restore, but the code inside them only executes when MSBuild subsequently evaluates and builds the project — restore alone doesn't run it. (Legacy &lt;code&gt;install.ps1&lt;/code&gt;/&lt;code&gt;init.ps1&lt;/code&gt; scripts are a separate, older mechanism scoped to &lt;code&gt;packages.config&lt;/code&gt; projects and the Visual Studio Package Manager; they don't apply to &lt;code&gt;PackageReference&lt;/code&gt; projects at all, and they don't fire on &lt;code&gt;dotnet restore&lt;/code&gt; either.) Researchers documented real campaigns exploiting the build-time mechanism — the "IAmReboot" wave in 2023 used malicious &lt;code&gt;.targets&lt;/code&gt; files that fired, in ReversingLabs' own words, "every time package A is built." That's actually a sharper contrast with Shai-Hulud than a restore-time claim would be: npm's payload runs at &lt;em&gt;install&lt;/em&gt; time, shipped as a &lt;code&gt;postinstall&lt;/code&gt; hook that &lt;code&gt;npm install&lt;/code&gt; executes automatically with no opt-out short of &lt;code&gt;--ignore-scripts&lt;/code&gt;. NuGet's equivalent mechanism fires one step later in the pipeline — a &lt;code&gt;dotnet build&lt;/code&gt; against a poisoned dependency can still execute attacker code before a single line of your own application code compiles, just not at the restore step itself.&lt;/p&gt;

&lt;p&gt;And there's no cooldown anywhere in the pipeline. A version published ninety seconds ago restores exactly the same as one published two years ago. Nothing in the default toolchain treats "brand new" as a signal worth pausing on — and plenty of documented malicious-package campaigns, on NuGet and npm alike, get caught by security researchers days to weeks after publication, not before.&lt;/p&gt;

&lt;h2&gt;
  
  
  What NuGate does about it
&lt;/h2&gt;

&lt;p&gt;We built NuGate as a dependency age gate. That's the whole scope, and we kept it that way deliberately: the build fails if any resolved package version — direct or transitive — was published less than a configurable number of days ago (seven, by default), unless it's explicitly allowlisted.&lt;/p&gt;

&lt;p&gt;It ships as two pieces. &lt;code&gt;NuGate.Tool&lt;/code&gt; is a dotnet CLI (&lt;code&gt;nugate check&lt;/code&gt;) meant to run in CI between &lt;code&gt;restore&lt;/code&gt; and &lt;code&gt;build&lt;/code&gt; — gating &lt;em&gt;before&lt;/em&gt; the next build ever runs, so a malicious &lt;code&gt;.targets&lt;/code&gt; file in a freshly restored package never gets the chance to execute. That directly targets the build-time-execution gap above. &lt;code&gt;NuGate.Build&lt;/code&gt; is an MSBuild task added as a single &lt;code&gt;PackageReference&lt;/code&gt; in &lt;code&gt;Directory.Build.props&lt;/code&gt;, gating every build in a repo including developers' own machines, hooked in after package assets resolve and before compilation starts. A thin GitHub Action wraps the CLI for workflow use.&lt;/p&gt;

&lt;p&gt;The age comes from nuget.org's immutable catalog &lt;code&gt;created&lt;/code&gt; timestamp — not the registration &lt;code&gt;published&lt;/code&gt; field, which nuget.org resets to a sentinel value when a version is unlisted. A version that gets pulled after a compromise is discovered is flagged as a violation regardless of its age, closing that particular loophole. Failures are closed by default: if the nuget.org API is unreachable and age can't be determined, that counts as a violation rather than a silent pass. Config lives in a single &lt;code&gt;nugate.json&lt;/code&gt; — minimum age, enforce vs. warn mode, an allowlist with optional expiry dates so exceptions don't quietly become permanent, and prefix exemptions for internal packages that never touch nuget.org.&lt;/p&gt;

&lt;p&gt;Equally important is what it doesn't do. NuGate does not detect malware, does not scan package contents, and does not prevent attacks. A compromised package that's thirty days old passes the gate exactly the same as any other dependency, by design. A cooldown shrinks the window in which a freshly poisoned version can reach a build — it does not close that window, and it says nothing about what's inside the package. Anyone evaluating it should read it as a narrow, boring control, not a security product.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where to find it
&lt;/h2&gt;

&lt;p&gt;We've released NuGate as open source, Apache-2.0, under the AbelSolutions-io GitHub organization. It installs from NuGet — &lt;code&gt;NuGate.Tool&lt;/code&gt; for CI, &lt;code&gt;NuGate.Build&lt;/code&gt; for the MSBuild integration. Issues and questions are welcome on the repo. There's a small landing page with a longer explanation of the cooldown model at &lt;a href="https://nugate.dev" rel="noopener noreferrer"&gt;nugate.dev&lt;/a&gt; if you want more than this post covers.&lt;/p&gt;

&lt;p&gt;No pricing, because there isn't any.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>security</category>
      <category>opensource</category>
      <category>nuget</category>
    </item>
  </channel>
</rss>
