<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Gitleaks on Nick Liu - Software Engineer</title><link>https://nick-liu.com/tags/gitleaks/</link><description>Recent content in Gitleaks on Nick Liu - Software Engineer</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>nickboy@users.noreply.github.com (Nick Liu)</managingEditor><webMaster>nickboy@users.noreply.github.com (Nick Liu)</webMaster><copyright>2026 Nick Liu</copyright><lastBuildDate>Wed, 05 Aug 2026 22:49:23 -0700</lastBuildDate><atom:link href="https://nick-liu.com/tags/gitleaks/index.xml" rel="self" type="application/rss+xml"/><item><title>Three layers of secret defense for a public dotfiles repo. One was decorative.</title><link>https://nick-liu.com/posts/public-dotfiles-secret-defense/</link><pubDate>Fri, 14 Aug 2026 00:00:00 +0000</pubDate><author>nickboy@users.noreply.github.com (Nick Liu)</author><guid>https://nick-liu.com/posts/public-dotfiles-secret-defense/</guid><description>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;
 
My dotfiles repo is public, which means any slip with a credential is permanent. History rewrites do not un-leak a key that a scraper already saw. So the defense cannot be one layer, and the interesting part of layering is not the count of tools. It is that each layer intercepts at a different moment: one before the commit exists, one at the moment of push, one sweeping the entire history in CI.

&lt;/div&gt;

&lt;p&gt;The uncomfortable part, and the reason this post belongs to this series: one of my three layers used to be a decoration.&lt;/p&gt;</description><content:encoded>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;
 
My dotfiles repo is public, which means any slip with a credential is permanent. History rewrites do not un-leak a key that a scraper already saw. So the defense cannot be one layer, and the interesting part of layering is not the count of tools. It is that each layer intercepts at a different moment: one before the commit exists, one at the moment of push, one sweeping the entire history in CI.

&lt;/div&gt;

&lt;p&gt;The uncomfortable part, and the reason this post belongs to this series: one of my three layers used to be a decoration.&lt;/p&gt;

&lt;h2 class="relative group"&gt;Three moments, not three tools
 &lt;div id="three-moments-not-three-tools" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#three-moments-not-three-tools" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;div class="not-prose diagram diagram-light"&gt;&lt;svg id="dfa157c3b1f735c20light" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" style="max-width: 1163.9375px;" viewBox="0 0 1163.9375 190" role="graphics-document document" aria-roledescription="flowchart-v2"&gt;&lt;style&gt;#dfa157c3b1f735c20light{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#dfa157c3b1f735c20light .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#dfa157c3b1f735c20light .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#dfa157c3b1f735c20light .error-icon{fill:rgb(255, 255, 255);}#dfa157c3b1f735c20light .error-text{fill:#000000;stroke:#000000;}#dfa157c3b1f735c20light .edge-thickness-normal{stroke-width:1px;}#dfa157c3b1f735c20light .edge-thickness-thick{stroke-width:3.5px;}#dfa157c3b1f735c20light .edge-pattern-solid{stroke-dasharray:0;}#dfa157c3b1f735c20light .edge-thickness-invisible{stroke-width:0;fill:none;}#dfa157c3b1f735c20light .edge-pattern-dashed{stroke-dasharray:3;}#dfa157c3b1f735c20light .edge-pattern-dotted{stroke-dasharray:2;}#dfa157c3b1f735c20light .marker{fill:rgb(74, 86, 92);stroke:rgb(74, 86, 92);}#dfa157c3b1f735c20light .marker.cross{stroke:rgb(74, 86, 92);}#dfa157c3b1f735c20light svg{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;}#dfa157c3b1f735c20light p{margin:0;}#dfa157c3b1f735c20light .label{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;color:#333;}#dfa157c3b1f735c20light .cluster-label text{fill:#000000;}#dfa157c3b1f735c20light .cluster-label span{color:#000000;}#dfa157c3b1f735c20light .cluster-label span p{background-color:transparent;}#dfa157c3b1f735c20light .label text,#dfa157c3b1f735c20light span{fill:#333;color:#333;}#dfa157c3b1f735c20light .node rect,#dfa157c3b1f735c20light .node circle,#dfa157c3b1f735c20light .node ellipse,#dfa157c3b1f735c20light .node polygon,#dfa157c3b1f735c20light .node path{fill:rgb(255, 255, 255);stroke:rgb(204, 216, 222);stroke-width:1px;}#dfa157c3b1f735c20light .rough-node .label text,#dfa157c3b1f735c20light .node .label text,#dfa157c3b1f735c20light .image-shape .label,#dfa157c3b1f735c20light .icon-shape .label{text-anchor:middle;}#dfa157c3b1f735c20light .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#dfa157c3b1f735c20light .rough-node .label,#dfa157c3b1f735c20light .node .label,#dfa157c3b1f735c20light .image-shape .label,#dfa157c3b1f735c20light .icon-shape .label{text-align:center;}#dfa157c3b1f735c20light .node.clickable{cursor:pointer;}#dfa157c3b1f735c20light .root .anchor path{fill:rgb(74, 86, 92)!important;stroke-width:0;stroke:rgb(74, 86, 92);}#dfa157c3b1f735c20light .arrowheadPath{fill:#000000;}#dfa157c3b1f735c20light .edgePath .path{stroke:rgb(74, 86, 92);stroke-width:1px;}#dfa157c3b1f735c20light .flowchart-link{stroke:rgb(74, 86, 92);fill:none;}#dfa157c3b1f735c20light .edgeLabel{background-color:rgb(182, 240, 255);text-align:center;}#dfa157c3b1f735c20light .edgeLabel p{background-color:rgb(182, 240, 255);}#dfa157c3b1f735c20light .edgeLabel rect{opacity:0.5;background-color:rgb(182, 240, 255);fill:rgb(182, 240, 255);}#dfa157c3b1f735c20light .labelBkg{background-color:rgba(182, 240, 255, 0.5);}#dfa157c3b1f735c20light .cluster rect{fill:rgb(255, 255, 255);stroke:rgb(129, 146, 154);stroke-width:1px;}#dfa157c3b1f735c20light .cluster text{fill:#000000;}#dfa157c3b1f735c20light .cluster span{color:#000000;}#dfa157c3b1f735c20light div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:12px;background:rgb(255, 255, 255);border:1px solid rgb(129, 146, 154);border-radius:2px;pointer-events:none;z-index:100;}#dfa157c3b1f735c20light .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#dfa157c3b1f735c20light rect.text{fill:none;stroke-width:0;}#dfa157c3b1f735c20light .icon-shape,#dfa157c3b1f735c20light .image-shape{background-color:rgb(182, 240, 255);text-align:center;}#dfa157c3b1f735c20light .icon-shape p,#dfa157c3b1f735c20light .image-shape p{background-color:rgb(182, 240, 255);padding:2px;}#dfa157c3b1f735c20light .icon-shape .label rect,#dfa157c3b1f735c20light .image-shape .label rect{opacity:0.5;background-color:rgb(182, 240, 255);fill:rgb(182, 240, 255);}#dfa157c3b1f735c20light .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#dfa157c3b1f735c20light .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#dfa157c3b1f735c20light .node .neo-node{stroke:rgb(204, 216, 222);}#dfa157c3b1f735c20light [data-look="neo"].node rect,#dfa157c3b1f735c20light [data-look="neo"].cluster rect,#dfa157c3b1f735c20light [data-look="neo"].node polygon{stroke:url(#dfa157c3b1f735c20light-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dfa157c3b1f735c20light [data-look="neo"].swimlane.cluster rect{filter:none;}#dfa157c3b1f735c20light [data-look="neo"].node path{stroke:url(#dfa157c3b1f735c20light-gradient);stroke-width:1px;}#dfa157c3b1f735c20light [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dfa157c3b1f735c20light [data-look="neo"].node .neo-line path{stroke:rgb(204, 216, 222);filter:none;}#dfa157c3b1f735c20light [data-look="neo"].node circle{stroke:url(#dfa157c3b1f735c20light-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dfa157c3b1f735c20light [data-look="neo"].node circle .state-start{fill:#000000;}#dfa157c3b1f735c20light [data-look="neo"].icon-shape .icon{fill:url(#dfa157c3b1f735c20light-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dfa157c3b1f735c20light [data-look="neo"].icon-shape .icon-neo path{stroke:url(#dfa157c3b1f735c20light-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dfa157c3b1f735c20light :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;marker id="dfa157c3b1f735c20light_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"&gt;&lt;path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20light_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"&gt;&lt;path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20light_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"&gt;&lt;path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20light_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"&gt;&lt;polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/polygon&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20light_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20light_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20light_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20light_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20light_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20light_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20light_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"&gt;&lt;path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20light_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"&gt;&lt;path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;g class="root"&gt;&lt;g class="clusters"&gt;&lt;/g&gt;&lt;g class="edgePaths"&gt;&lt;path d="M268,95L272.167,95C276.333,95,284.667,95,292.333,95C300,95,307,95,310.5,95L314,95" id="dfa157c3b1f735c20light-L_A_B_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_A_B_0" data-points="W3sieCI6MjY4LCJ5Ijo5NX0seyJ4IjoyOTMsInkiOjk1fSx7IngiOjMxOCwieSI6OTV9XQ==" data-look="classic" marker-end="url(#dfa157c3b1f735c20light_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M574.219,95L578.385,95C582.552,95,590.885,95,598.552,95C606.219,95,613.219,95,616.719,95L620.219,95" id="dfa157c3b1f735c20light-L_B_C_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_B_C_0" data-points="W3sieCI6NTc0LjIxODc1LCJ5Ijo5NX0seyJ4Ijo1OTkuMjE4NzUsInkiOjk1fSx7IngiOjYyNC4yMTg3NSwieSI6OTV9XQ==" data-look="classic" marker-end="url(#dfa157c3b1f735c20light_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M884.219,95L888.385,95C892.552,95,900.885,95,908.552,95C916.219,95,923.219,95,926.719,95L930.219,95" id="dfa157c3b1f735c20light-L_C_D_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_C_D_0" data-points="W3sieCI6ODg0LjIxODc1LCJ5Ijo5NX0seyJ4Ijo5MDkuMjE4NzUsInkiOjk1fSx7IngiOjkzNC4yMTg3NSwieSI6OTV9XQ==" data-look="classic" marker-end="url(#dfa157c3b1f735c20light_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g class="edgeLabels"&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_A_B_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_B_C_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_C_D_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="nodes"&gt;&lt;g class="node default" id="dfa157c3b1f735c20light-flowchart-A-0" data-look="classic" transform="translate(138, 95)"&gt;&lt;rect class="basic label-container" style="" x="-130" y="-87" width="260" height="174"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-100, -72)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="200" height="144"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;commit-time&lt;br&gt;gitleaks scans the staged diff&lt;br&gt;inside the yadm pre-commit hook&lt;br&gt;(~50ms)&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="dfa157c3b1f735c20light-flowchart-B-1" data-look="classic" transform="translate(446.109375, 95)"&gt;&lt;rect class="basic label-container" style="" x="-128.109375" y="-63" width="256.21875" height="126"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-98.109375, -48)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="196.21875" height="96"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;push-time&lt;br&gt;GitHub push protection,&lt;br&gt;server-side, at the moment&lt;br&gt;of push&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="dfa157c3b1f735c20light-flowchart-C-3" data-look="classic" transform="translate(754.21875, 95)"&gt;&lt;rect class="basic label-container" style="" x="-130" y="-63" width="260" height="126"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-100, -48)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="200" height="96"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;CI-time&lt;br&gt;TruffleHog sweeps the FULL&lt;br&gt;git history, blocking&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="dfa157c3b1f735c20light-flowchart-D-5" data-look="classic" transform="translate(1045.078125, 95)"&gt;&lt;rect class="basic label-container" style="" x="-110.859375" y="-39" width="221.71875" height="78"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-80.859375, -24)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="161.71875" height="48"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;plus: repo-level secret&lt;br&gt;scanning enabled&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;defs&gt;&lt;filter id="dfa157c3b1f735c20light-drop-shadow" height="130%" width="130%"&gt;&lt;feDropShadow dx="4" dy="4" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;defs&gt;&lt;filter id="dfa157c3b1f735c20light-drop-shadow-small" height="150%" width="150%"&gt;&lt;feDropShadow dx="2" dy="2" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;linearGradient id="dfa157c3b1f735c20light-gradient" gradientUnits="objectBoundingBox" x1="0%" y1="0%" x2="100%" y2="0%"&gt;&lt;stop offset="0%" stop-color="rgb(204, 216, 222)" stop-opacity="1"&gt;&lt;/stop&gt;&lt;stop offset="100%" stop-color="rgb(28, 209, 255)" stop-opacity="1"&gt;&lt;/stop&gt;&lt;/linearGradient&gt;&lt;/svg&gt;&lt;/div&gt;
 &lt;div class="not-prose diagram diagram-dark"&gt;&lt;svg id="dfa157c3b1f735c20dark" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" style="max-width: 1163.9375px;" viewBox="0 0 1163.9375 190" role="graphics-document document" aria-roledescription="flowchart-v2"&gt;&lt;style&gt;#dfa157c3b1f735c20dark{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;fill:#ccc;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#dfa157c3b1f735c20dark .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#dfa157c3b1f735c20dark .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#dfa157c3b1f735c20dark .error-icon{fill:#a44141;}#dfa157c3b1f735c20dark .error-text{fill:#ddd;stroke:#ddd;}#dfa157c3b1f735c20dark .edge-thickness-normal{stroke-width:1px;}#dfa157c3b1f735c20dark .edge-thickness-thick{stroke-width:3.5px;}#dfa157c3b1f735c20dark .edge-pattern-solid{stroke-dasharray:0;}#dfa157c3b1f735c20dark .edge-thickness-invisible{stroke-width:0;fill:none;}#dfa157c3b1f735c20dark .edge-pattern-dashed{stroke-dasharray:3;}#dfa157c3b1f735c20dark .edge-pattern-dotted{stroke-dasharray:2;}#dfa157c3b1f735c20dark .marker{fill:lightgrey;stroke:lightgrey;}#dfa157c3b1f735c20dark .marker.cross{stroke:lightgrey;}#dfa157c3b1f735c20dark svg{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;}#dfa157c3b1f735c20dark p{margin:0;}#dfa157c3b1f735c20dark .label{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;color:#ccc;}#dfa157c3b1f735c20dark .cluster-label text{fill:#F9FFFE;}#dfa157c3b1f735c20dark .cluster-label span{color:#F9FFFE;}#dfa157c3b1f735c20dark .cluster-label span p{background-color:transparent;}#dfa157c3b1f735c20dark .label text,#dfa157c3b1f735c20dark span{fill:#ccc;color:#ccc;}#dfa157c3b1f735c20dark .node rect,#dfa157c3b1f735c20dark .node circle,#dfa157c3b1f735c20dark .node ellipse,#dfa157c3b1f735c20dark .node polygon,#dfa157c3b1f735c20dark .node path{fill:#1f2020;stroke:#ccc;stroke-width:1px;}#dfa157c3b1f735c20dark .rough-node .label text,#dfa157c3b1f735c20dark .node .label text,#dfa157c3b1f735c20dark .image-shape .label,#dfa157c3b1f735c20dark .icon-shape .label{text-anchor:middle;}#dfa157c3b1f735c20dark .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#dfa157c3b1f735c20dark .rough-node .label,#dfa157c3b1f735c20dark .node .label,#dfa157c3b1f735c20dark .image-shape .label,#dfa157c3b1f735c20dark .icon-shape .label{text-align:center;}#dfa157c3b1f735c20dark .node.clickable{cursor:pointer;}#dfa157c3b1f735c20dark .root .anchor path{fill:lightgrey!important;stroke-width:0;stroke:lightgrey;}#dfa157c3b1f735c20dark .arrowheadPath{fill:lightgrey;}#dfa157c3b1f735c20dark .edgePath .path{stroke:lightgrey;stroke-width:1px;}#dfa157c3b1f735c20dark .flowchart-link{stroke:lightgrey;fill:none;}#dfa157c3b1f735c20dark .edgeLabel{background-color:hsl(0, 0%, 34.4117647059%);text-align:center;}#dfa157c3b1f735c20dark .edgeLabel p{background-color:hsl(0, 0%, 34.4117647059%);}#dfa157c3b1f735c20dark .edgeLabel rect{opacity:0.5;background-color:hsl(0, 0%, 34.4117647059%);fill:hsl(0, 0%, 34.4117647059%);}#dfa157c3b1f735c20dark .labelBkg{background-color:rgba(87.75, 87.75, 87.75, 0.5);}#dfa157c3b1f735c20dark .cluster rect{fill:hsl(180, 1.5873015873%, 28.3529411765%);stroke:rgba(255, 255, 255, 0.25);stroke-width:1px;}#dfa157c3b1f735c20dark .cluster text{fill:#F9FFFE;}#dfa157c3b1f735c20dark .cluster span{color:#F9FFFE;}#dfa157c3b1f735c20dark div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:12px;background:hsl(20, 1.5873015873%, 12.3529411765%);border:1px solid rgba(255, 255, 255, 0.25);border-radius:2px;pointer-events:none;z-index:100;}#dfa157c3b1f735c20dark .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#ccc;}#dfa157c3b1f735c20dark rect.text{fill:none;stroke-width:0;}#dfa157c3b1f735c20dark .icon-shape,#dfa157c3b1f735c20dark .image-shape{background-color:hsl(0, 0%, 34.4117647059%);text-align:center;}#dfa157c3b1f735c20dark .icon-shape p,#dfa157c3b1f735c20dark .image-shape p{background-color:hsl(0, 0%, 34.4117647059%);padding:2px;}#dfa157c3b1f735c20dark .icon-shape .label rect,#dfa157c3b1f735c20dark .image-shape .label rect{opacity:0.5;background-color:hsl(0, 0%, 34.4117647059%);fill:hsl(0, 0%, 34.4117647059%);}#dfa157c3b1f735c20dark .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#dfa157c3b1f735c20dark .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#dfa157c3b1f735c20dark .node .neo-node{stroke:#ccc;}#dfa157c3b1f735c20dark [data-look="neo"].node rect,#dfa157c3b1f735c20dark [data-look="neo"].cluster rect,#dfa157c3b1f735c20dark [data-look="neo"].node polygon{stroke:url(#dfa157c3b1f735c20dark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dfa157c3b1f735c20dark [data-look="neo"].swimlane.cluster rect{filter:none;}#dfa157c3b1f735c20dark [data-look="neo"].node path{stroke:url(#dfa157c3b1f735c20dark-gradient);stroke-width:1px;}#dfa157c3b1f735c20dark [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dfa157c3b1f735c20dark [data-look="neo"].node .neo-line path{stroke:#ccc;filter:none;}#dfa157c3b1f735c20dark [data-look="neo"].node circle{stroke:url(#dfa157c3b1f735c20dark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dfa157c3b1f735c20dark [data-look="neo"].node circle .state-start{fill:#000000;}#dfa157c3b1f735c20dark [data-look="neo"].icon-shape .icon{fill:url(#dfa157c3b1f735c20dark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dfa157c3b1f735c20dark [data-look="neo"].icon-shape .icon-neo path{stroke:url(#dfa157c3b1f735c20dark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dfa157c3b1f735c20dark :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;marker id="dfa157c3b1f735c20dark_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"&gt;&lt;path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20dark_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"&gt;&lt;path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20dark_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"&gt;&lt;path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20dark_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"&gt;&lt;polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/polygon&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20dark_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20dark_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20dark_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20dark_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20dark_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20dark_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20dark_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"&gt;&lt;path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="dfa157c3b1f735c20dark_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"&gt;&lt;path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;g class="root"&gt;&lt;g class="clusters"&gt;&lt;/g&gt;&lt;g class="edgePaths"&gt;&lt;path d="M268,95L272.167,95C276.333,95,284.667,95,292.333,95C300,95,307,95,310.5,95L314,95" id="dfa157c3b1f735c20dark-L_A_B_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_A_B_0" data-points="W3sieCI6MjY4LCJ5Ijo5NX0seyJ4IjoyOTMsInkiOjk1fSx7IngiOjMxOCwieSI6OTV9XQ==" data-look="classic" marker-end="url(#dfa157c3b1f735c20dark_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M574.219,95L578.385,95C582.552,95,590.885,95,598.552,95C606.219,95,613.219,95,616.719,95L620.219,95" id="dfa157c3b1f735c20dark-L_B_C_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_B_C_0" data-points="W3sieCI6NTc0LjIxODc1LCJ5Ijo5NX0seyJ4Ijo1OTkuMjE4NzUsInkiOjk1fSx7IngiOjYyNC4yMTg3NSwieSI6OTV9XQ==" data-look="classic" marker-end="url(#dfa157c3b1f735c20dark_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M884.219,95L888.385,95C892.552,95,900.885,95,908.552,95C916.219,95,923.219,95,926.719,95L930.219,95" id="dfa157c3b1f735c20dark-L_C_D_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_C_D_0" data-points="W3sieCI6ODg0LjIxODc1LCJ5Ijo5NX0seyJ4Ijo5MDkuMjE4NzUsInkiOjk1fSx7IngiOjkzNC4yMTg3NSwieSI6OTV9XQ==" data-look="classic" marker-end="url(#dfa157c3b1f735c20dark_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g class="edgeLabels"&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_A_B_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_B_C_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_C_D_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="nodes"&gt;&lt;g class="node default" id="dfa157c3b1f735c20dark-flowchart-A-0" data-look="classic" transform="translate(138, 95)"&gt;&lt;rect class="basic label-container" style="" x="-130" y="-87" width="260" height="174"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-100, -72)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="200" height="144"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;commit-time&lt;br&gt;gitleaks scans the staged diff&lt;br&gt;inside the yadm pre-commit hook&lt;br&gt;(~50ms)&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="dfa157c3b1f735c20dark-flowchart-B-1" data-look="classic" transform="translate(446.109375, 95)"&gt;&lt;rect class="basic label-container" style="" x="-128.109375" y="-63" width="256.21875" height="126"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-98.109375, -48)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="196.21875" height="96"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;push-time&lt;br&gt;GitHub push protection,&lt;br&gt;server-side, at the moment&lt;br&gt;of push&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="dfa157c3b1f735c20dark-flowchart-C-3" data-look="classic" transform="translate(754.21875, 95)"&gt;&lt;rect class="basic label-container" style="" x="-130" y="-63" width="260" height="126"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-100, -48)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="200" height="96"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table; white-space: break-spaces; line-height: 1.5; max-width: 200px; text-align: center; width: 200px;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;CI-time&lt;br&gt;TruffleHog sweeps the FULL&lt;br&gt;git history, blocking&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="dfa157c3b1f735c20dark-flowchart-D-5" data-look="classic" transform="translate(1045.078125, 95)"&gt;&lt;rect class="basic label-container" style="" x="-110.859375" y="-39" width="221.71875" height="78"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-80.859375, -24)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="161.71875" height="48"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;plus: repo-level secret&lt;br&gt;scanning enabled&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;defs&gt;&lt;filter id="dfa157c3b1f735c20dark-drop-shadow" height="130%" width="130%"&gt;&lt;feDropShadow dx="4" dy="4" stdDeviation="0" flood-opacity="0.06" flood-color="#FFFFFF"&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;defs&gt;&lt;filter id="dfa157c3b1f735c20dark-drop-shadow-small" height="150%" width="150%"&gt;&lt;feDropShadow dx="2" dy="2" stdDeviation="0" flood-opacity="0.06" flood-color="#FFFFFF"&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;linearGradient id="dfa157c3b1f735c20dark-gradient" gradientUnits="objectBoundingBox" x1="0%" y1="0%" x2="100%" y2="0%"&gt;&lt;stop offset="0%" stop-color="#cccccc" stop-opacity="1"&gt;&lt;/stop&gt;&lt;stop offset="100%" stop-color="hsl(180, 0%, 18.3529411765%)" stop-opacity="1"&gt;&lt;/stop&gt;&lt;/linearGradient&gt;&lt;/svg&gt;&lt;/div&gt;
&lt;p&gt;The first layer stops a leak before it ever enters history, which is the only place a leak is still cheap. It runs in the same pre-commit hook that the first post in this series brought back from the dead, and costs about 50ms:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Staged-diff secret scan (GitHub push protection is the server-side&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# backstop; this stops leaks before they enter history at all).&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# NOTE: AWS&amp;#39;s documentation example keys are allowlisted by gitleaks&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# default config — verified detection with realistic-looking canaries.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;command&lt;/span&gt; -v gitleaks &amp;gt;/dev/null 2&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; ! &lt;span class="nv"&gt;GIT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;yadm introspect repo&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="nv"&gt;GIT_WORK_TREE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; gitleaks git --pre-commit --staged --no-banner --redact &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The second layer is GitHub&amp;rsquo;s push protection, which I do not have to maintain and cannot forget to run. The third is the one with the embarrassing past.&lt;/p&gt;

&lt;h2 class="relative group"&gt;The scanner that could not fail
 &lt;div id="the-scanner-that-could-not-fail" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-scanner-that-could-not-fail" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;The old TruffleHog step in CI used a &lt;code&gt;base..head&lt;/code&gt; diff mode. On a direct push to master, base equals head, so the scan range was empty and the step passed by definition. It could not find anything because it was not looking at anything.&lt;/p&gt;
&lt;p&gt;Better still, the step wore &lt;code&gt;continue-on-error&lt;/code&gt;, added at some point because it kept failing for unrelated reasons. So the pipeline contained a secret scanner that scanned nothing and whose verdict was ignored anyway. Two independent ways to be decorative, stacked.&lt;/p&gt;
&lt;p&gt;The current version scans the full history and blocks:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# BLOCKING secret scan over the FULL git history (not a base..head&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# diff — the old diff mode failed with base==head on master pushes,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# which is why it was continue-on-error, i.e. decorative).&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# --results=verified: only credentials that TruffleHog actively&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;# confirmed against the provider fail the build (no regex noise).&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Check for secrets (blocking)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;trufflesecurity/trufflehog@6f3c981e7b77f235fd2702dd74af25fc4b72bf11&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;extra_args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--&lt;span class="l"&gt;results=verified&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code&gt;--results=verified&lt;/code&gt; flag deserves its sentence. A blocking step that cries wolf on regex noise gets demoted back to advisory within a month, by the same social process that produced &lt;code&gt;continue-on-error&lt;/code&gt; last time. Only credentials that TruffleHog actually confirmed against the provider fail the build, so a red result means something real, so the red stays blocking.&lt;/p&gt;

&lt;h2 class="relative group"&gt;The workflows are attack surface too
 &lt;div id="the-workflows-are-attack-surface-too" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-workflows-are-attack-surface-too" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;A secret scanner running inside a compromised workflow is not much of a defense, so the pipeline got the same treatment as the content:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every GitHub Action is pinned to a commit SHA. The &lt;code&gt;tj-actions/changed-files&lt;/code&gt; compromise made the argument better than I can: a mutable tag means executing whatever upstream&amp;rsquo;s HEAD is at run time.&lt;/li&gt;
&lt;li&gt;Dependabot keeps the pins current, and proved it was alive by filing its first PR within seconds of the merge.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;zizmor&lt;/code&gt; and &lt;code&gt;actionlint&lt;/code&gt; lint the workflows themselves. Every checkout sets &lt;code&gt;persist-credentials: false&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GITHUB_TOKEN&lt;/code&gt; gets least privilege, &lt;code&gt;contents: read&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;permissions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;read&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Checkout repository&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;persist-credentials&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 class="relative group"&gt;Identity: no private key on disk
 &lt;div id="identity-no-private-key-on-disk" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#identity-no-private-key-on-disk" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;The strongest way to not leak a private key from your dotfiles is for the key to not be a file. GitHub auth goes over SSH through the 1Password agent, so no private key exists on disk. The same key signs commits via &lt;code&gt;op-ssh-sign&lt;/code&gt;. The public halves live in a tracked &lt;code&gt;allowed_signers&lt;/code&gt;; the signing configuration stays in the untracked &lt;code&gt;~/.gitconfig&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Git Credential Manager got retired in the process, and it left a landmine: a stale &lt;code&gt;osxkeychain&lt;/code&gt; cached credential that makes HTTPS remotes fail with &amp;ldquo;Invalid username or token&amp;rdquo;. The fix is not to repair the credential. The fix is to stop using the road:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# &amp;#34;Invalid username or token&amp;#34; does not mean a broken credential;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# it means the HTTPS route itself is retired&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;yadm remote set-url origin git@github.com:&amp;lt;owner&amp;gt;/&amp;lt;repo&amp;gt;.git&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 class="relative group"&gt;The three pits that actually cost time
 &lt;div id="the-three-pits-that-actually-cost-time" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-three-pits-that-actually-cost-time" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;A signing key is not an authentication key.&lt;/strong&gt; GitHub keeps those as two separate lists. Add your 1Password key as a signing key only, and commits sign beautifully while every &lt;code&gt;push&lt;/code&gt; fails. The error does not hint at this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;IdentitiesOnly yes&lt;/code&gt; mutes the agent.&lt;/strong&gt; My global SSH defaults set it, which makes ssh ignore any key the agent offers. Hosts that authenticate through 1Password need it explicitly off:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep -A2 &lt;span class="s2"&gt;&amp;#34;^Host github.com&amp;#34;&lt;/span&gt; ~/.ssh/config.d/10-github-1password.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# this host needs IdentitiesOnly no, or ssh never sees the agent&amp;#39;s key&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;The example key that proves nothing.&lt;/strong&gt; I wanted a negative test for the gitleaks layer, so the obvious move is planting &lt;code&gt;AKIAIOSFODNN7EXAMPLE&lt;/code&gt; and watching it get caught. Except gitleaks&amp;rsquo; default config allowlists AWS&amp;rsquo;s documentation example keys, so that test passes vacuously. The canary has to look real, and it has to be assembled at runtime so the repo never contains a secret-shaped literal:&lt;/p&gt;
&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;aws_key = &amp;#34;%s%s&amp;#34;\n&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;AKIA&amp;#34;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ZZZQK9X2M4P7L3TQ&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$GL_TMP&lt;/span&gt;&lt;span class="s2"&gt;/leak.txt&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Testing your defenses without tripping them yourself takes more thought than building them.&lt;/p&gt;

&lt;h2 class="relative group"&gt;Why three layers
 &lt;div id="why-three-layers" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#why-three-layers" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;p&gt;The number itself does nothing. What matters is that each layer has an independent failure mode, and they fail at different times. The hook can be bypassed with &lt;code&gt;--no-verify&lt;/code&gt; in a hurry. Push protection only sees patterns GitHub knows. The CI sweep runs after the fact. Any single layer being wrong leaves two others standing at different points on the timeline, and the previous two posts in this series are extended demonstrations of how quietly a single layer can be wrong.&lt;/p&gt;

&lt;h2 class="relative group"&gt;Lessons
 &lt;div id="lessons" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#lessons" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Layered defense means layered in time: before the commit, at the push, over the history. Three tools at the same moment is one layer with extra steps.&lt;/li&gt;
&lt;li&gt;A scan step that cannot fail is not security. It is a green light bulb.&lt;/li&gt;
&lt;li&gt;Make blocking checks precise enough to stay blocking. Noise is how gates get demoted to advisory.&lt;/li&gt;
&lt;li&gt;Verify each defense by attacking it with something realistic. The obvious test credential is exactly the one your scanner was configured to ignore.&lt;/li&gt;
&lt;li&gt;Pin your actions to SHAs. A mutable tag is someone else&amp;rsquo;s deploy button pointed at your CI.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 class="relative group"&gt;References
 &lt;div id="references" class="anchor"&gt;&lt;/div&gt;
 
 &lt;span
 class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;
 &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#references" aria-label="Anchor"&gt;#&lt;/a&gt;
 &lt;/span&gt;
 
&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/gitleaks/gitleaks" target="_blank" rel="noreferrer"&gt;gitleaks&lt;/a&gt; (staged-diff scanning; the default-config allowlist of AWS documentation keys is in its bundled config)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/trufflesecurity/trufflehog" target="_blank" rel="noreferrer"&gt;TruffleHog&lt;/a&gt; (&lt;code&gt;--results=verified&lt;/code&gt; behavior)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/code-security/secret-scanning/introduction/about-push-protection" target="_blank" rel="noreferrer"&gt;GitHub push protection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/advisories/GHSA-mrrh-fwg8-r2c3" target="_blank" rel="noreferrer"&gt;tj-actions/changed-files compromise, CVE-2025-30066&lt;/a&gt; (the case for SHA-pinning actions)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/zizmorcore/zizmor" target="_blank" rel="noreferrer"&gt;zizmor&lt;/a&gt; and &lt;a href="https://github.com/rhysd/actionlint" target="_blank" rel="noreferrer"&gt;actionlint&lt;/a&gt; (workflow linting)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.1password.com/docs/ssh/" target="_blank" rel="noreferrer"&gt;1Password SSH agent&lt;/a&gt; and &lt;a href="https://developer.1password.com/docs/ssh/git-commit-signing/" target="_blank" rel="noreferrer"&gt;commit signing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;The repo: &lt;a href="https://github.com/nickboy/dotfiles" target="_blank" rel="noreferrer"&gt;nickboy/dotfiles&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item></channel></rss>