<?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: Zarif Latif</title>
    <description>The latest articles on DEV Community by Zarif Latif (@zarif_latif_114a9ed729d58).</description>
    <link>https://dev.to/zarif_latif_114a9ed729d58</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%2F4187909%2F7d7a992c-8a62-467e-8644-c768f38f5505.jpg</url>
      <title>DEV Community: Zarif Latif</title>
      <link>https://dev.to/zarif_latif_114a9ed729d58</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zarif_latif_114a9ed729d58"/>
    <language>en</language>
    <item>
      <title>A pentest report is the start of the work. Here is what the end should look like.</title>
      <dc:creator>Zarif Latif</dc:creator>
      <pubDate>Fri, 16 Oct 2026 05:57:55 +0000</pubDate>
      <link>https://dev.to/zarif_latif_114a9ed729d58/a-pentest-report-is-the-start-of-the-work-here-is-what-the-end-should-look-like-4hag</link>
      <guid>https://dev.to/zarif_latif_114a9ed729d58/a-pentest-report-is-the-start-of-the-work-here-is-what-the-end-should-look-like-4hag</guid>
      <description>&lt;p&gt;A pentest report lists what is wrong. What you show an auditor, or an enterprise customer's security team, is the opposite: what was wrong, what changed, and the proof it worked. Teams usually have the first document and have to build the second in a hurry.&lt;/p&gt;

&lt;p&gt;This is a practical list of what that second document should hold, taken from a real exercise we ran on OWASP NodeGoat. We are not auditors, and every auditor and customer asks for slightly different things, so check yours. But the pattern below is what most reviewers want to be able to follow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Six things to keep for every finding
&lt;/h2&gt;

&lt;p&gt;For each finding in the report, someone reading your records later should be able to find:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;What it was.&lt;/strong&gt; A stable ID, the weakness (for example CWE-601, open redirect), and the file and line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;That it was in scope.&lt;/strong&gt; The list of findings you agreed to close, fixed in writing before the work started, so nothing is added or quietly dropped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What changed.&lt;/strong&gt; A pull request, one per weakness, whose description says what changed and why.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who made and reviewed the change.&lt;/strong&gt; The author, and your own team's merge.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;That it worked.&lt;/strong&gt; A rescan after the fix showing the finding is gone and nothing new appeared.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When.&lt;/strong&gt; The merge date, so the record shows the finding was closed inside the audit period.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A spreadsheet with those six columns, one row per finding, is most of what a reviewer asks for. The pull requests and the rescan output are the proof behind each row.&lt;/p&gt;

&lt;h2&gt;
  
  
  A real row, and the one we did not change
&lt;/h2&gt;

&lt;p&gt;On 14 October we ran a rehearsal Fix Sprint on OWASP NodeGoat, a deliberately vulnerable Node.js app. Sixteen findings were in scope. Fifteen were fixed in five pull requests: six by Railo's rule-based fixes, nine written by hand by an engineer. One was explained instead of changed. The evidence pack tells you which is which.&lt;/p&gt;

&lt;p&gt;Here is one row, as it appears in the pack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;ID:&lt;/strong&gt; F-004&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Weakness:&lt;/strong&gt; open redirect (CWE-601)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Where:&lt;/strong&gt; &lt;code&gt;app/routes/index.js&lt;/code&gt;, line 72&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fixed by:&lt;/strong&gt; Railo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check:&lt;/strong&gt; finding gone, nothing new&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pull request:&lt;/strong&gt; 1&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merged:&lt;/strong&gt; 2026-10-14&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The finding that was not fixed is the more interesting one. F-007 is a TLS private key committed to the repository. The tempting fix is to delete the file. That does not help: the key stays in git history and has to be treated as exposed. So the pack records it as "explained, not changed", with the real recommendation: create a new key and certificate, keep them outside the repository, remove the file, and rewrite history if the repository is or was ever public.&lt;/p&gt;

&lt;p&gt;A final scan of the merged code found only that one finding. An auditor can read that honestly: everything was closed except one item, and the reason it is still listed is written down.&lt;/p&gt;

&lt;p&gt;The full pack is public, with the CSV: &lt;a href="https://railo.dev/sample-evidence" rel="noopener noreferrer"&gt;railo.dev/sample-evidence&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three ways closing a finding goes wrong
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Closing it by deleting the evidence.&lt;/strong&gt; Removing a file, a line or a scanner rule makes the finding disappear without fixing anything. Records should show a change that removes the weakness, checked by a rescan.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merging without a rescan.&lt;/strong&gt; A fix can remove one finding and introduce another, or break the build. The rescan is what shows the finding is gone and nothing new appeared, and your own CI on the change is still the real test.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One giant pull request.&lt;/strong&gt; Forty fixes in a single change are hard to review and impossible to trace. One pull request per weakness lets a reviewer read each fix, and lets the record point from each finding to its own change.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How a Fix Sprint produces this
&lt;/h2&gt;

&lt;p&gt;We built our Fix Sprint around exactly this record. You send the findings, from a pentest report, a scanner export or a customer questionnaire, and we agree in writing which ones are in scope. In one week we return a pull request per weakness. Railo's rule-based fixes cover the common patterns; an engineer writes the rest by hand and reads where the code is used before changing it. Every fix is rescanned. Your team reviews and merges, and we finish with the evidence pack: one row per finding, as above.&lt;/p&gt;

&lt;p&gt;The price is fixed at $1,500 for one repository. Nothing is merged without your review, and nothing from your repository is executed on our side.&lt;/p&gt;

&lt;p&gt;If you have a report to close, email &lt;a href="mailto:zarif.latif@railo.dev"&gt;zarif.latif@railo.dev&lt;/a&gt; with the subject "Railo Fix Sprint", or book 15 minutes at &lt;a href="https://cal.com/railo" rel="noopener noreferrer"&gt;cal.com/railo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Zarif Latif, founder, Railo&lt;/p&gt;

</description>
      <category>security</category>
      <category>compliance</category>
      <category>devops</category>
      <category>startup</category>
    </item>
    <item>
      <title>How We Tested Railo on 11 Public Repositories</title>
      <dc:creator>Zarif Latif</dc:creator>
      <pubDate>Fri, 16 Oct 2026 05:54:21 +0000</pubDate>
      <link>https://dev.to/zarif_latif_114a9ed729d58/how-we-tested-railo-on-11-public-repositories-34i2</link>
      <guid>https://dev.to/zarif_latif_114a9ed729d58/how-we-tested-railo-on-11-public-repositories-34i2</guid>
      <description>&lt;p&gt;Railo is a GitHub Action that finds security issues in a pull request and proposes the fix. The fixes are rule-based code transforms, not model output: the same input always gives the same patch, and every patch is a diff you can read.&lt;/p&gt;

&lt;p&gt;Until this month we measured fix quality on a benchmark of examples we wrote ourselves. That number looked good, and it was also the easiest test we could have chosen. So we ran the exact pipeline the Action runs on eleven public repositories we did not write, and read every fix it proposed.&lt;/p&gt;

&lt;p&gt;The short version:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;923 findings across the eleven repositories.&lt;/li&gt;
&lt;li&gt;162 proposed fixes. We read all of them.&lt;/li&gt;
&lt;li&gt;95 of those 162 should not have been proposed. Most were edits to vendored libraries, test data and seed data.&lt;/li&gt;
&lt;li&gt;None of the 162 broke the syntax of a file. Wrong is not the same as broken, and wrong was the problem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post is the list of what went wrong and what we changed. We think it is more useful than another benchmark score.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we ran it
&lt;/h2&gt;

&lt;p&gt;We picked two kinds of repository on purpose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deliberately vulnerable apps&lt;/strong&gt;, where real findings are guaranteed: OWASP NodeGoat, OWASP Juice Shop, PyGoat, DVWA, OWASP WebGoat and govwa.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Well-maintained projects&lt;/strong&gt;, where almost every finding should be noise: Flask, Express, Gin, Slim and Spring Petclinic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For each one:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Shallow clone, and scan the whole repository rather than a pull request diff.&lt;/li&gt;
&lt;li&gt;Run the Action's own code path: load the config, scan with the pinned Semgrep version (1.102.0), generate fixes, collect the changed files.&lt;/li&gt;
&lt;li&gt;Parse every changed file before and after the fix with the language's own tooling: Python's &lt;code&gt;compile&lt;/code&gt;, the TypeScript parser for JavaScript and TypeScript, &lt;code&gt;gofmt -e&lt;/code&gt;, &lt;code&gt;php -l&lt;/code&gt;, and &lt;code&gt;javac&lt;/code&gt; stopped after parsing. A fix that adds a parse error counts as breaking the file.&lt;/li&gt;
&lt;li&gt;Read every proposed fix by hand.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One thing we switched off: the Action normally tries the project's own test command before and after each fix. In this harness we did not execute anything from the repositories.&lt;/p&gt;

&lt;p&gt;We now re-run this harness, and read the diffs, before every Action release.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;Two things stand out: almost all the wrong fixes landed in code the project did not write or does not ship, and on the well-maintained projects the right number of fixes was zero.&lt;/p&gt;

&lt;p&gt;Findings, then fixes proposed before and after our changes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;WebGoat: 381 findings, 95 fixes before, 15 after&lt;/li&gt;
&lt;li&gt;Juice Shop: 216 findings, 11 before, 6 after&lt;/li&gt;
&lt;li&gt;PyGoat: 132 findings, 16 before, 16 after&lt;/li&gt;
&lt;li&gt;DVWA: 94 findings, 27 before, 24 after&lt;/li&gt;
&lt;li&gt;NodeGoat: 24 findings, 10 before, 6 after&lt;/li&gt;
&lt;li&gt;govwa: 8 findings, 0 before, 0 after&lt;/li&gt;
&lt;li&gt;Flask: 22 findings, 1 before, 0 after&lt;/li&gt;
&lt;li&gt;Gin: 20 findings, 1 before, 0 after&lt;/li&gt;
&lt;li&gt;Express: 14 findings, 0 before, 0 after&lt;/li&gt;
&lt;li&gt;Spring Petclinic: 9 findings, 1 before, 0 after&lt;/li&gt;
&lt;li&gt;Slim: 3 findings, 0 before, 0 after&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Total: 923 findings, 162 fixes before, 67 after&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;WebGoat alone accounts for 80 of the 95 removed fixes, and about 77 of those were in two vendored editor files.&lt;/p&gt;

&lt;p&gt;A follow-up release then cut noise on the maintained projects: Flask went from 22 findings to 11, and NodeGoat from 24 to 21, with one more correct fix (7 instead of 6).&lt;/p&gt;

&lt;h2&gt;
  
  
  What we got wrong
&lt;/h2&gt;

&lt;p&gt;Every problem below was a fix that parsed, looked plausible in isolation, and was wrong in context. Each case now goes to a person instead: the finding is still reported, but no change is proposed. Each has a regression test.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Rewriting vendored libraries
&lt;/h3&gt;

&lt;p&gt;WebGoat ships the Ace editor in &lt;code&gt;static/js/libs/&lt;/code&gt;. Our hard-coded-secret rule saw lines like &lt;code&gt;token: "keyword"&lt;/code&gt; in Ace's syntax-highlighting rules and turned them into &lt;code&gt;process.env.TOKEN&lt;/code&gt;. About 77 fixes, all nonsense. Juice Shop's vendored &lt;code&gt;three.js&lt;/code&gt; was edited too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now:&lt;/strong&gt; &lt;code&gt;lib/&lt;/code&gt; and &lt;code&gt;libs/&lt;/code&gt; under static asset folders, minified and bundled files, and any JavaScript file over 5,000 lines are treated as third-party code.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Treating test data as production
&lt;/h3&gt;

&lt;p&gt;Gin keeps a certificate and key in &lt;code&gt;testdata/&lt;/code&gt; for its TLS tests. We "fixed" them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now:&lt;/strong&gt; &lt;code&gt;testdata&lt;/code&gt;, &lt;code&gt;__tests__&lt;/code&gt;, &lt;code&gt;__mocks__&lt;/code&gt;, &lt;code&gt;mocks&lt;/code&gt;, &lt;code&gt;e2e&lt;/code&gt;, &lt;code&gt;cypress&lt;/code&gt;, &lt;code&gt;bower_components&lt;/code&gt; and similar folders are skipped.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Collapsing seed data into one variable
&lt;/h3&gt;

&lt;p&gt;NodeGoat's &lt;code&gt;db-reset.js&lt;/code&gt; creates three users with different passwords. We gave all three the same &lt;code&gt;process.env.PASSWORD&lt;/code&gt;. Juice Shop's &lt;code&gt;users.yml&lt;/code&gt; was worse: 27 users, all set to the literal string &lt;code&gt;${PASSWORD}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now:&lt;/strong&gt; several different values for the same key in one file mean data, not a secret.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Writing &lt;code&gt;${VAR}&lt;/code&gt; where nothing substitutes it
&lt;/h3&gt;

&lt;p&gt;We rewrote a value in Petclinic's Kubernetes manifest, and in Juice Shop's YAML data, to &lt;code&gt;${VAR}&lt;/code&gt;. Neither file is ever processed by anything that expands it, so the app would have read the literal text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now:&lt;/strong&gt; only Docker Compose files and Spring Boot config files get &lt;code&gt;${VAR}&lt;/code&gt;, because those are the formats that substitute it.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. &lt;code&gt;process.env&lt;/code&gt; in browser code
&lt;/h3&gt;

&lt;p&gt;Angular components and in-browser lesson scripts got &lt;code&gt;process.env.SECRET&lt;/code&gt;. There is no &lt;code&gt;process.env&lt;/code&gt; in a browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now:&lt;/strong&gt; browser code, detected by path, file extension, or use of &lt;code&gt;document&lt;/code&gt;, &lt;code&gt;window&lt;/code&gt; or &lt;code&gt;localStorage&lt;/code&gt; outside string literals, is not rewritten. (The first version of this check mistook a server config file that mentioned &lt;code&gt;document.write&lt;/code&gt; inside a string for browser code. Fixed in the follow-up release.)&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Names that only look like secrets
&lt;/h3&gt;

&lt;p&gt;A smart-contract address called &lt;code&gt;BeeTokenAddress&lt;/code&gt;. DVWA's &lt;code&gt;$token = "userid:2"&lt;/code&gt;, which is an attack payload, not a credential. Ace's token types again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now:&lt;/strong&gt; names ending in address, id, url, host, name, type and similar are left alone, as are &lt;code&gt;token&lt;/code&gt; values that are not shaped like a credential, unless the value matches a known provider format (AWS, GitHub, Slack, Stripe, Google keys), which is caught anywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. &lt;code&gt;innerHTML&lt;/code&gt; to &lt;code&gt;textContent&lt;/code&gt; on HTML that is meant to be HTML
&lt;/h3&gt;

&lt;p&gt;Juice Shop renders Markdown to HTML with &lt;code&gt;snarkdown&lt;/code&gt; and assigns it to &lt;code&gt;innerHTML&lt;/code&gt;; WebGoat builds tables as HTML strings. Switching to &lt;code&gt;textContent&lt;/code&gt; would print the tags as text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now:&lt;/strong&gt; values that are HTML by construction are not rewritten.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. Flask &lt;code&gt;Markup(value)&lt;/code&gt; to &lt;code&gt;escape(value)&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This double-escapes markup that Flask already treats as safe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now:&lt;/strong&gt; &lt;code&gt;Markup()&lt;/code&gt; with no request data in it is not rewritten.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. "Escaping" a JSONP callback with &lt;code&gt;htmlspecialchars&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;It makes the code look handled without making the callback safe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Now:&lt;/strong&gt; this goes to a person.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is still not good enough
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Noise on well-maintained code.&lt;/strong&gt; After the follow-up release Flask still produces 11 findings, all of which we read as noise: 7 from a generic open-redirect rule, 2 from a &lt;code&gt;compile()&lt;/code&gt; rule, one Markup and one SHA-1. No fix is proposed for any of them, but in warn mode they still show up on Flask's pull requests. That's the next thing we are working on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Partial detection.&lt;/strong&gt; In DVWA's &lt;code&gt;view_help.php&lt;/code&gt;, the &lt;code&gt;if&lt;/code&gt; branch is flagged and fixed and the identical &lt;code&gt;else&lt;/code&gt; branch is not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context a rule cannot see.&lt;/strong&gt; Making Juice Shop's &lt;code&gt;token&lt;/code&gt; cookie &lt;code&gt;httpOnly&lt;/code&gt; is correct in general and breaks Juice Shop, whose frontend reads that cookie. We offer it as a suggestion for review, not an automatic change, which is where it belongs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not automated, by design.&lt;/strong&gt; NodeGoat's &lt;code&gt;eval(req.body.preTax)&lt;/code&gt; is detected and explained, but a person writes the fix. Some fixes need a person, and we would rather say so than guess.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you maintain one of these projects and think we have something wrong, we would like to hear it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;The Action is free to use and needs no account or key. It covers Python, JavaScript and TypeScript, Java, Go and PHP. Start in &lt;code&gt;warn&lt;/code&gt; mode: it comments the proposed fixes on the pull request and changes nothing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.head_ref }}&lt;/span&gt;
    &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IWEBai/railo-action@v2&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;warn&lt;/span&gt;
    &lt;span class="na"&gt;base_branch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ github.base_ref }}&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The full workflow, with the permissions it needs, is on the &lt;a href="https://github.com/IWEBai/railo-action" rel="noopener noreferrer"&gt;Action's page&lt;/a&gt;. If you run it on your own code and it proposes something wrong, please open an issue with the diff: that is exactly the kind of case this post is made of.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you would rather not fix it yourself
&lt;/h2&gt;

&lt;p&gt;The Action covers the common, pattern-based findings. Plenty of real findings are not that: a pentest report with forty items, a scanner export before an audit, a customer's security questionnaire. For those we run a &lt;strong&gt;Fix Sprint&lt;/strong&gt;: you send us the findings, and in one week you get a pull request per weakness. Some of the fixes Railo writes; the rest an engineer writes by hand, and the evidence pack says which. Every fix is rescanned, and your team reviews and merges everything. It is a fixed price of $1,500 for one repository.&lt;/p&gt;

&lt;p&gt;The result is an evidence pack: one row per finding, with the file, the fix, who made it, the rescan result and the merge date. We ran a rehearsal on OWASP NodeGoat, and the finished pack is public: &lt;a href="https://railo.dev/sample-evidence" rel="noopener noreferrer"&gt;railo.dev/sample-evidence&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you have findings to close, email &lt;a href="mailto:zarif.latif@railo.dev"&gt;zarif.latif@railo.dev&lt;/a&gt; with the subject "Railo Fix Sprint", or book 15 minutes at &lt;a href="https://cal.com/railo" rel="noopener noreferrer"&gt;cal.com/railo&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Zarif Latif, founder, Railo&lt;/p&gt;

</description>
      <category>security</category>
      <category>github</category>
      <category>opensource</category>
      <category>python</category>
    </item>
  </channel>
</rss>
