<?xml version="1.0" encoding="utf-8" standalone="yes"?><?xml-stylesheet type="text/xsl" href="https://perrotta.dev/rss.xsl"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Security on ¬ just serendipity 🍀</title>
    <link>https://perrotta.dev/</link>
    <description>Recent content in Security on ¬ just serendipity 🍀</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <managingEditor>serendipity@perrotta.dev (Thiago Perrotta)</managingEditor>
    <webMaster>serendipity@perrotta.dev (Thiago Perrotta)</webMaster>
    <copyright>© 2013 - 2026 Thiago Perrotta ·
  a fork of [hugo ʕ•ᴥ•ʔ bear](https://github.com/janraasch/hugo-bearblog/)
</copyright>
    <lastBuildDate>Mon, 27 Jul 2026 17:47:45 +0200</lastBuildDate>
    <atom:link href="https://perrotta.dev/tags/security/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>pi: update pinned npm packages
      </title>
      <link>https://perrotta.dev/2026/07/pi-update-pinned-npm-packages/</link>
      <pubDate>Mon, 27 Jul 2026 14:28:59 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>ai</category>
      <category>coding</category>
      <category>dev</category>
      <category>security</category>
      <guid>https://perrotta.dev/2026/07/pi-update-pinned-npm-packages/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: pin &lt;a href=&#34;https://pi.dev/&#34;&gt;pi&lt;/a&gt; npm packages without giving up&#xA;convenient updates.&lt;/p&gt;&#xA;&lt;p&gt;My&#xA;&lt;a href=&#34;https://github.com/thiagowfx/.dotfiles/blob/master/pi/.pi/agent/settings.json&#34;&gt;&lt;code&gt;settings.json&lt;/code&gt;&lt;/a&gt;&#xA;used to leave some package versions unpinned, until now:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-diff&#34;&gt;     &amp;#34;npm:@heyhuynhgiabuu/pi-diff@0.7.6&amp;#34;,&#xA;-    &amp;#34;npm:@tifan/pi-recap&amp;#34;,&#xA;-    &amp;#34;npm:@tmustier/pi-tab-status&amp;#34;,&#xA;-    &amp;#34;npm:@tintinweb/pi-subagents&amp;#34;&#xA;&amp;#43;    &amp;#34;npm:@tifan/pi-recap@0.4.4&amp;#34;,&#xA;&amp;#43;    &amp;#34;npm:@tmustier/pi-tab-status@0.1.4&amp;#34;,&#xA;&amp;#43;    &amp;#34;npm:@tintinweb/pi-subagents@0.14.3&amp;#34;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Exact versions make package installation reproducible (&amp;amp; safer), but Pi then&#xA;does not automatically pull newer packages.&lt;/p&gt;&#xA;&lt;p&gt;To tackle that, I added a &lt;code&gt;just update-pi&lt;/code&gt; recipe that asks npm for each&#xA;package&amp;rsquo;s current &lt;code&gt;latest&lt;/code&gt; version and writes back an exact pin:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-just&#34;&gt;[doc(&amp;#39;Update pinned Pi npm packages&amp;#39;)]&#xA;[group(&amp;#39;update&amp;#39;)]&#xA;update-pi:&#xA;    #!/usr/bin/env bash&#xA;    set -euo pipefail&#xA;&#xA;    settings=&amp;#34;pi/.pi/agent/settings.json&amp;#34;&#xA;    updates=&amp;#34;{}&amp;#34;&#xA;    while IFS= read -r spec; do&#xA;        if [[ &amp;#34;$spec&amp;#34; =~ ^npm:((@[^/]&amp;#43;/[^@]&amp;#43;)|([^@]&amp;#43;))(@[^@]&amp;#43;)?$ ]]; then&#xA;            package=&amp;#34;${BASH_REMATCH[1]}&amp;#34;&#xA;        else&#xA;            echo &amp;#34;Invalid npm package spec: $spec&amp;#34; &amp;gt;&amp;amp;2&#xA;            exit 1&#xA;        fi&#xA;&#xA;        latest=&amp;#34;$(npm view &amp;#34;$package@latest&amp;#34; version)&amp;#34;&#xA;        pinned=&amp;#34;npm:$package@$latest&amp;#34;&#xA;        updates=&amp;#34;$(jq -cn \&#xA;            --argjson updates &amp;#34;$updates&amp;#34; \&#xA;            --arg spec &amp;#34;$spec&amp;#34; \&#xA;            --arg pinned &amp;#34;$pinned&amp;#34; \&#xA;            &amp;#39;$updates &amp;#43; {($spec): $pinned}&amp;#39;)&amp;#34;&#xA;        echo &amp;#34;✓ $pinned&amp;#34;&#xA;    done &amp;lt; &amp;lt;(jq -r &amp;#39;.packages[] | select(type == &amp;#34;string&amp;#34; and startswith(&amp;#34;npm:&amp;#34;))&amp;#39; &amp;#34;$settings&amp;#34;)&#xA;&#xA;    updated_settings=&amp;#34;$(jq --argjson updates &amp;#34;$updates&amp;#34; \&#xA;        &amp;#39;.packages |= map($updates[.] // .)&amp;#39; &amp;#34;$settings&amp;#34;)&amp;#34;&#xA;    printf &amp;#39;%s\n&amp;#39; &amp;#34;$updated_settings&amp;#34; &amp;gt; &amp;#34;$settings&amp;#34;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;It handles every &lt;code&gt;npm:&lt;/code&gt; entry, including scoped packages:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% just update-pi&#xA;✓ npm:@heyhuynhgiabuu/pi-diff@0.7.6&#xA;✓ npm:@heyhuynhgiabuu/pi-pretty@0.6.20&#xA;✓ npm:@tifan/pi-recap@0.4.4&#xA;✓ npm:@tmustier/pi-tab-status@0.1.4&#xA;✓ npm:@tintinweb/pi-subagents@0.14.3&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The aggregate &lt;code&gt;just update&lt;/code&gt; recipe invokes it alongside submodule, prek hook,&#xA;and vendored-file updates.&lt;/p&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;🤖 &lt;em&gt;Drafted with &lt;code&gt;/bloggify&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: pi: update pinned npm packages&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/ai/&#34;&gt;#ai&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/coding/&#34;&gt;#coding&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>atuin: delete account
      </title>
      <link>https://perrotta.dev/2026/05/atuin-delete-account/</link>
      <pubDate>Mon, 18 May 2026 17:49:39 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>pkm</category>
      <category>privacy</category>
      <category>security</category>
      <category>selfhosted</category>
      <guid>https://perrotta.dev/2026/05/atuin-delete-account/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://perrotta.dev/2025/11/atuin/&#34;&gt;Previously&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve been &lt;a href=&#34;https://perrotta.dev/2026/01/atuin-new-machine-setup/&#34;&gt;self-hosting&lt;/a&gt;&#xA;&lt;a href=&#34;https://atuin.sh/&#34;&gt;&lt;code&gt;atuin&lt;/code&gt;&lt;/a&gt; for quite a while now. As such, I no longer need an&#xA;account in the official server (created a long time ago).&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Problem statement&lt;/strong&gt;: delete that account.&lt;/p&gt;&#xA;&lt;p&gt;Note that I am currently logged into my self-hosted server.&lt;/p&gt;&#xA;&lt;p&gt;As such, the first step is to switch back to the official server:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% $EDITOR ~/.config/atuin/config.toml&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Then ensure that &lt;code&gt;sync_address&lt;/code&gt; is either commented out or set to&#xA;&lt;code&gt;https://api.atuin.sh&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Now log in to it:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% atuin login --username {username}&#xA;&amp;gt;&amp;gt; insert password, insert key&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;And, finally:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% atuin account delete&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Afterwards, switch back to the self-hosted server and run &lt;code&gt;atuin login&lt;/code&gt; again.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: atuin: delete account&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/pkm/&#34;&gt;#pkm&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/privacy/&#34;&gt;#privacy&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/selfhosted/&#34;&gt;#selfhosted&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>cco: OAuth refresh fix
      </title>
      <link>https://perrotta.dev/2026/04/cco-oauth-refresh-fix/</link>
      <pubDate>Sun, 12 Apr 2026 07:35:32 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>ai</category>
      <category>dev</category>
      <category>security</category>
      <guid>https://perrotta.dev/2026/04/cco-oauth-refresh-fix/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://perrotta.dev/2026/03/cco-additionaldirectories/&#34;&gt;Previously&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Problem statement&lt;/strong&gt;: &lt;code&gt;cco&lt;/code&gt; is not able to autonomously refresh the OAuth token&#xA;used by &lt;code&gt;claude&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The manual workaround is to run an one-off unsandboxed &lt;code&gt;claude&lt;/code&gt;, and then get&#xA;back to &lt;code&gt;cco&lt;/code&gt; afterwards.&lt;/p&gt;&#xA;&lt;p&gt;Why?&lt;/p&gt;&#xA;&lt;p&gt;Run &lt;code&gt;/doctor&lt;/code&gt; within &lt;code&gt;cco&lt;/code&gt; and compare its output with &lt;code&gt;/doctor&lt;/code&gt; within an&#xA;unsandbox claude code session. The culprit:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;Warning: macOS Keychain is not writable (security: SecKeychainItemCreateFromContent (&amp;lt;default&amp;gt;): UNIX[Operation not permitted];&#xA;  add-generic-password: returned 100001). Console login will fail to save your API key.&#xA;  Fix: Run: security unlock-keychain ~/Library/Keychains/login.keychain-db — if that doesn&amp;#39;t fix it, your login keychain password may&#xA;  be out of sync with your account password: open Keychain Access, select the &amp;#39;login&amp;#39; keychain, then Edit → Change Password for&#xA;  Keychain &amp;#39;login&amp;#39;.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;So now I am passing &lt;code&gt;--add-dir ~/Library/Keychains&lt;/code&gt; to &lt;code&gt;cco&lt;/code&gt;, and the warning is&#xA;finally gone!&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-diff&#34;&gt;commit 5c111a7caffb4e6b3dc4d678d4fc1bd6c8296ae5&#xA;Author: Thiago Perrotta &amp;lt;{redacted}&amp;gt;&#xA;Date:   Sun Apr 12 07:34:53 2026 &amp;#43;0200&#xA;&#xA;    cco: add key chain dir, via /doctor&#xA;&#xA;diff --git profile/.profile.d/alias.sh profile/.profile.d/alias.sh&#xA;index 0f5018c..80d7b60 100644&#xA;--- profile/.profile.d/alias.sh&#xA;&amp;#43;&amp;#43;&amp;#43; profile/.profile.d/alias.sh&#xA;@@ -51,7 &amp;#43;51,7 @@ fi&#xA;&#xA; if command -v claude &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then&#xA; &#x9;if command -v cco &amp;gt;/dev/null 2&amp;gt;&amp;amp;1; then&#xA;-&#x9;&#x9;alias claudey=&amp;#34;cco --allow-oauth-refresh --add-dir ~/.cache --add-dir ~/.aws/cli/cache --add-dir ~/.aws/sso/cache --add-dir ~/.azure --add-dir ~/.terraform.d/plugin-cache --add-dir ~/go&amp;#34;&#xA;&amp;#43;&#x9;&#x9;alias claudey=&amp;#34;cco --allow-oauth-refresh --add-dir ~/.cache --add-dir ~/.aws/cli/cache --add-dir ~/.aws/sso/cache --add-dir ~/.azure --add-dir ~/.terraform.d/plugin-cache --add-dir ~/go --add-dir ~/Library/Keychains&amp;#34;&#xA; &#x9;fi&#xA; &#x9;alias claudeyy=&amp;#34;claude --allow-dangerously-skip-permissions&amp;#34;&#xA; fi&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: cco: OAuth refresh fix&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/ai/&#34;&gt;#ai&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Obfuscate your email
      </title>
      <link>https://perrotta.dev/2026/04/obfuscate-your-email/</link>
      <pubDate>Sun, 05 Apr 2026 19:30:54 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>privacy</category>
      <category>security</category>
      <category>selfhosted</category>
      <guid>https://perrotta.dev/2026/04/obfuscate-your-email/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://ev-fae.quest/obfuscate-your-email/&#34;&gt;Evie Fae&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve had trouble with email addresses visible on the public web getting&#xA;snatched by bots and peppered with endless spam&lt;/p&gt;&#xA;&lt;p&gt;[&amp;hellip;]&lt;/p&gt;&#xA;&lt;p&gt;It turns out that most spam bots aren&amp;rsquo;t all that sophisticated, and if you&#xA;wanted to write something on your page like: Send an email to me at&#xA;evie[DOT]fae[AT]mydomain[DOT]com then you don&amp;rsquo;t have much to worry about when&#xA;it comes to email harvesters. I don&amp;rsquo;t actually like this solution because it&#xA;introduces too much friction into the communication process.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Evie shares a user-friendly workflow to obfuscate email addresses in public&#xA;websites such as the personal blog you&amp;rsquo;re reading.&lt;/p&gt;&#xA;&lt;p&gt;I&#xA;&lt;a href=&#34;https://github.com/thiagowfx/thiagowfx.github.io/commit/228180f6bb4d36cc2fe64a3ad12c4d5fabae4ebd&#34;&gt;just&lt;/a&gt;&#xA;enabled it. Win-win!&lt;/p&gt;&#xA;&lt;p&gt;I asked the LLM to write an &lt;a href=&#34;https://perrotta.dev/2026/01/adrs-and-llms/&#34;&gt;ADR&lt;/a&gt; about&#xA;the&#xA;&lt;a href=&#34;https://github.com/thiagowfx/thiagowfx.github.io/blob/master/docs/adrs/0011-email-obfuscation.md&#34;&gt;implementation&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Obfuscate your email&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/privacy/&#34;&gt;#privacy&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/selfhosted/&#34;&gt;#selfhosted&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>cco: additionalDirectories
      </title>
      <link>https://perrotta.dev/2026/03/cco-additionaldirectories/</link>
      <pubDate>Wed, 18 Mar 2026 17:31:13 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>ai</category>
      <category>dev</category>
      <category>security</category>
      <guid>https://perrotta.dev/2026/03/cco-additionaldirectories/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://perrotta.dev/2026/02/cco-claude-condom-sandbox/&#34;&gt;Previously&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Problem statement&lt;/strong&gt;: passing &lt;code&gt;--add-dir&lt;/code&gt; flags to&#xA;&lt;a href=&#34;https://github.com/nikvdp/cco&#34;&gt;cco&lt;/a&gt; in a shell alias doesn&amp;rsquo;t scale well as&#xA;the number of directories increases.&lt;/p&gt;&#xA;&lt;p&gt;I had this alias:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;alias claudey=&amp;#34;cco --allow-oauth-refresh --add-dir ~/.cache --add-dir ~/.aws/cli/cache --add-dir ~/.aws/sso/cache --add-dir ~/.azure --add-dir ~/.terraform.d/plugin-cache&amp;#34;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;code&gt;cco&lt;/code&gt; now supports reading &lt;code&gt;additionalDirectories&lt;/code&gt; from&#xA;&lt;code&gt;.claude/settings.local.json&lt;/code&gt;, inside the &lt;code&gt;permissions&lt;/code&gt; object:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;{&#xA;  &amp;#34;permissions&amp;#34;: {&#xA;    &amp;#34;additionalDirectories&amp;#34;: [&#xA;      &amp;#34;~/.cache&amp;#34;,&#xA;      &amp;#34;~/.aws/cli/cache&amp;#34;,&#xA;      &amp;#34;~/.aws/sso/cache&amp;#34;,&#xA;      &amp;#34;~/.azure&amp;#34;,&#xA;      &amp;#34;~/.terraform.d/plugin-cache&amp;#34;&#xA;    ]&#xA;  }&#xA;}&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&amp;hellip;since &lt;a href=&#34;https://github.com/nikvdp/cco/pull/44&#34;&gt;PR #44&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The alias then becomes:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;alias claudey=&amp;#34;cco --allow-oauth-refresh&amp;#34;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The directories are now project-specific, checked into each repo&amp;rsquo;s&#xA;&lt;code&gt;.claude/settings.local.json&lt;/code&gt;. Much cleaner.&lt;/p&gt;&#xA;&lt;p&gt;I wish it would support the global &lt;code&gt;~/.claude/settings.json&lt;/code&gt; too, but it&#xA;doesn&amp;rsquo;t.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: cco: additionalDirectories&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/ai/&#34;&gt;#ai&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Bypass copy protection in PDFs
      </title>
      <link>https://perrotta.dev/2026/03/bypass-copy-protection-in-pdfs/</link>
      <pubDate>Thu, 12 Mar 2026 12:02:23 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>ai</category>
      <category>dev</category>
      <category>security</category>
      <guid>https://perrotta.dev/2026/03/bypass-copy-protection-in-pdfs/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://perrotta.dev/2025/10/grep-in-pdfs/&#34;&gt;Previously&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Problem statement&lt;/strong&gt;: Open &lt;code&gt;contract.pdf&lt;/code&gt; in Preview.app in macOS. Copy a text&#xA;portion in it. A dialog pops up:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Without the owner password, you do not have permission to copy portions of the&#xA;document.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Enter the password to unlock copying from the document.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Excuse me? I &lt;em&gt;do not&lt;/em&gt; need your permission.&lt;/p&gt;&#xA;&lt;h2 id=&#34;attempt-1-claude&#34;&gt;&#xA;  Attempt #1: Claude&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2026/03/bypass-copy-protection-in-pdfs/#attempt-1-claude&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Open Claude Code. Prompt:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;output it in markdown, translated to English&lt;/p&gt;&#xA;&lt;p&gt;output a summary in the beginning, then output the full translation afterwards&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;It gets stuck. Boooo!&lt;/p&gt;&#xA;&lt;p&gt;There&amp;rsquo;s nothing more annoying than a PC&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;https://perrotta.dev/2026/03/bypass-copy-protection-in-pdfs/#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; LLM that fails silently without&#xA;telling you why.&lt;/p&gt;&#xA;&lt;h2 id=&#34;attempt-2-codex&#34;&gt;&#xA;  Attempt #2: Codex&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2026/03/bypass-copy-protection-in-pdfs/#attempt-2-codex&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Open Codex. Use the same prompt.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;• I&amp;#39;m locating Vertrag-Entwurf.pdf and extracting its text first so I can translate it accurately and format the result in&#xA;  Markdown.&#xA;&#xA;• Explored&#xA;  └ List rg --files -g Vertrag-Entwurf.pdf&#xA;    List Vertrag-Entwurf.pdf&#xA;&#xA;• The file is present. I&amp;#39;m extracting the PDF text now and checking whether the layout needs OCR-style cleanup before translation.&#xA;&#xA;• Ran which pdftotext&#xA;  └ /opt/homebrew/bin/pdftotext&#xA;&#xA;• Ran pdfinfo /Users/thiago.perrotta/Downloads/Vertrag-Entwurf.pdf&#xA;  └ Creator:         Chromium&#xA;    Producer:        {redacted}&#xA;    … &amp;#43;14 lines&#xA;    Optimized:       no&#xA;    PDF version:     1.4&#xA;&#xA;• The PDF has text content but copy protection is enabled, so I&amp;#39;m testing extraction directly. If that fails, I&amp;#39;ll fall back to OCR&#xA;  from rendered pages.&#xA;&#xA;• Ran pdftotext /Users/thiago.perrotta/Downloads/Vertrag-Entwurf.pdf -&#xA;  └ {redacted}&#xA;&#xA;    … &amp;#43;1264 lines&#xA;&#xA;• # Summary&#xA;&#xA;[...]&#xA;&#xA;# Full Translation&#xA;&#xA;[...]&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This is why we need competition.&lt;/p&gt;&#xA;&lt;p&gt;This is why we need diversity.&lt;/p&gt;&#xA;&lt;p&gt;In this context, OpenAI is the&#xA;&lt;a href=&#34;https://openai.com/index/our-agreement-with-the-department-of-war/&#34;&gt;non-PC&lt;/a&gt;&#xA;model.&lt;/p&gt;&#xA;&lt;p&gt;Do you see the problem with arbitrarily defining what is and isn&amp;rsquo;t PC? Who gets&#xA;to have this privilege? Why? Is it universal?&lt;/p&gt;&#xA;&lt;p&gt;DRM in PDFs suck.&lt;/p&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;Politically correct.&amp;#160;&lt;a href=&#34;https://perrotta.dev/2026/03/bypass-copy-protection-in-pdfs/#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Bypass copy protection in PDFs&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/ai/&#34;&gt;#ai&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Topgrade
      </title>
      <link>https://perrotta.dev/2026/03/topgrade/</link>
      <pubDate>Thu, 12 Mar 2026 02:20:30 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>security</category>
      <guid>https://perrotta.dev/2026/03/topgrade/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://topgrade-rs.github.io/&#34;&gt;Topgrade&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Keeping your system up-to-date usually involves invoking multiple package&#xA;managers. This results in big, non-portable shell one-liners saved in your&#xA;shell. To remedy this, Topgrade detects which tools you use and runs the&#xA;appropriate commands to update them.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;They are essentially a full-featured version of my &lt;a href=&#34;https://perrotta.dev/2025/10/new-script-sd-world-perform-a-full-system-upgrade/&#34;&gt;&lt;code&gt;sd_world&lt;/code&gt;&lt;/a&gt; script.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;: Install it with your &lt;a href=&#34;https://repology.org/project/topgrade/versions&#34;&gt;favorite&lt;/a&gt; package manager and then run &lt;code&gt;topgrade&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Sample run on my system, to give you an idea of how comprehensive it is:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;── 02:21:07 - Summary ──────────────────────────────────────────────&#xA;Brew (ARM): FAILED&#xA;Brew Cask (ARM): OK&#xA;App Store: OK&#xA;System update: OK&#xA;Visual Studio Code extensions: OK&#xA;myrepos: OK&#xA;vim: OK&#xA;Neovim: OK&#xA;npm: OK&#xA;yarn: OK&#xA;Containers: FAILED&#xA;helm: OK&#xA;gem: FAILED&#xA;rubygems: FAILED&#xA;GitHub CLI Extensions: OK&#xA;Git Repositories: FAILED&#xA;Claude Code: OK&#xA;uv: OK&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This is not the full output, it&amp;rsquo;s only the summary at the end.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Topgrade&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>claude: srt: sandbox runtime
      </title>
      <link>https://perrotta.dev/2026/03/claude-srt-sandbox-runtime/</link>
      <pubDate>Wed, 04 Mar 2026 12:27:42 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>ai</category>
      <category>dev</category>
      <category>security</category>
      <guid>https://perrotta.dev/2026/03/claude-srt-sandbox-runtime/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://perrotta.dev/2026/02/cco-claude-condom-sandbox/&#34;&gt;Previously: &lt;code&gt;cco&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;&lt;code&gt;cco&lt;/code&gt; handles filesystem isolation but not networking — &lt;code&gt;srt&lt;/code&gt; adds network&#xA;isolation.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;: This post was adapted from a slides presentation I recently&#xA;created for an AI hackathon. Its prose resembles a quick-reference manual.&lt;/p&gt;&#xA;&lt;h2 id=&#34;srt-sandbox-runtime&#34;&gt;&#xA;  &lt;code&gt;srt&lt;/code&gt;: sandbox-runtime&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2026/03/claude-srt-sandbox-runtime/#srt-sandbox-runtime&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/anthropic-experimental/sandbox-runtime&#34;&gt;&lt;code&gt;srt&lt;/code&gt;&lt;/a&gt; is&#xA;Anthropic&amp;rsquo;s open-source sandbox runtime. It is process agnostic and it uses&#xA;OS-native primitives —&#xA;&lt;a href=&#34;https://www.chromium.org/developers/design-documents/sandbox/osx-sandboxing-design/&#34;&gt;Seatbelt&lt;/a&gt;&#xA;on macOS, &lt;a href=&#34;https://github.com/containers/bubblewrap&#34;&gt;bubblewrap&lt;/a&gt; on Linux — no&#xA;container overhead, just like &lt;code&gt;cco&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Run a command inside the sandbox (&amp;ldquo;hello world&amp;rdquo;):&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% npx @anthropic-ai/sandbox-runtime &amp;lt;command&amp;gt;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Or you might as well install it so that it is easily invokable as &lt;code&gt;srt&lt;/code&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;https://perrotta.dev/2026/03/claude-srt-sandbox-runtime/#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% npm install -g @anthropic-ai/sandbox-runtime&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;code&gt;-g&lt;/code&gt; is for global (=not local).&lt;/p&gt;&#xA;&lt;p&gt;Network restrictions in action:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% srt &amp;#34;curl anthropic.com&amp;#34;&#xA;&amp;lt;html&amp;gt;...&amp;lt;/html&amp;gt;                  # allowed&#xA;&#xA;% srt &amp;#34;curl example.com&amp;#34;&#xA;Connection blocked by network allowlist  # blocked&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Filesystem restrictions in action:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% srt &amp;#34;cat README.md&amp;#34;&#xA;# Anthropic Sandb...               # current dir allowed&#xA;&#xA;% srt &amp;#34;cat ~/.ssh/id_rsa&amp;#34;&#xA;cat: Operation not permitted        # blocked&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The sandbox configuration lives globally in &lt;code&gt;~/.srt-settings.json&lt;/code&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;{&#xA;  &amp;#34;network&amp;#34;: {&#xA;    &amp;#34;allowedDomains&amp;#34;: [&amp;#34;github.com&amp;#34;, &amp;#34;registry.npmjs.org&amp;#34;],&#xA;    &amp;#34;deniedDomains&amp;#34;: []&#xA;  },&#xA;  &amp;#34;filesystem&amp;#34;: {&#xA;    &amp;#34;denyRead&amp;#34;: [&amp;#34;~/.ssh&amp;#34;, &amp;#34;~/.aws/credentials&amp;#34;],&#xA;    &amp;#34;allowWrite&amp;#34;: [&amp;#34;.&amp;#34;],&#xA;    &amp;#34;denyWrite&amp;#34;: [&amp;#34;.env&amp;#34;, &amp;#34;config/production.json&amp;#34;]&#xA;  }&#xA;}&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;It is secure by default — everything is blocked unless explicitly allowed.&#xA;Separate controls for filesystem reads, writes, and network domains.&lt;/p&gt;&#xA;&lt;h2 id=&#34;native-sandbox&#34;&gt;&#xA;  native &lt;code&gt;/sandbox&lt;/code&gt;&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2026/03/claude-srt-sandbox-runtime/#native-sandbox&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Since &lt;a href=&#34;https://code.claude.com/docs/en/sandboxing&#34;&gt;v1.0.29&lt;/a&gt;, Claude Code ships&#xA;with native sandboxing — powered by &lt;code&gt;srt&lt;/code&gt; under the hood. Enable it with the&#xA;&lt;code&gt;/sandbox&lt;/code&gt; command. Zero setup on macOS.&lt;/p&gt;&#xA;&lt;p&gt;Two modes:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Auto-allow&lt;/strong&gt;: sandboxed commands run without prompts; unsandboxable commands&#xA;fall back to the regular permission flow.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Regular&lt;/strong&gt;: all commands still require approval, but sandbox boundaries are&#xA;enforced. The most &lt;strong&gt;strict&lt;/strong&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;In the first mode, you will see the following pattern quite often:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;⏺ Bash(gh api user --jq .login)&#xA;  ⎿  Error: Exit code 71&#xA;     sandbox-exec: sandbox_apply: Operation not permitted&#xA;&#xA;     sandbox-exec: sandbox_apply: Operation not permitted&#xA;&#xA;⏺ Sandbox restriction on gh. Let me retry outside the sandbox.&#xA;&#xA;⏺ Bash(gh api user --jq .login)&#xA;  ⎿  thiagowfx&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Anthropic reports ~84% fewer permission prompts with auto-allow mode.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;~/.claude/settings.json&lt;/code&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;{&#xA;  &amp;#34;sandbox&amp;#34;: {&#xA;    &amp;#34;enabled&amp;#34;: true,&#xA;    &amp;#34;autoAllowBashIfSandboxed&amp;#34;: true,&#xA;    &amp;#34;allowUnsandboxedCommands&amp;#34;: true,&#xA;    &amp;#34;allowedDomains&amp;#34;: [&amp;#34;registry.npmjs.org&amp;#34;, &amp;#34;api.github.com&amp;#34;],&#xA;    &amp;#34;excludedCommands&amp;#34;: [&amp;#34;docker&amp;#34;],&#xA;    &amp;#34;filesystem&amp;#34;: {&#xA;      &amp;#34;allowWrite&amp;#34;: [&amp;#34;~/.kube&amp;#34;, &amp;#34;/tmp/build&amp;#34;, &amp;#34;~/.cache&amp;#34;]&#xA;    }&#xA;  }&#xA;}&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;autoAllowBashIfSandboxed&lt;/code&gt; — sandboxed commands run without prompts&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;excludedCommands&lt;/code&gt; — tools that always run outside the sandbox (e.g. Docker)&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;allowUnsandboxedCommands&lt;/code&gt; — escape hatch for commands that fail inside the&#xA;sandbox (still requires approval)&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;filesystem.allowWrite&lt;/code&gt; — grant subprocess write access to paths outside the&#xA;working directory&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;When to use what?&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;cco&lt;/code&gt;&lt;/strong&gt;: quick filesystem-only sandbox, zero config, IDE agnostic&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;srt&lt;/code&gt;&lt;/strong&gt;: standalone sandbox for any process (not only Claude Code) with&#xA;network isolation&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;/sandbox&lt;/code&gt;&lt;/strong&gt;: native Claude Code integration, fewest prompts, recommended&#xA;for daily use&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;I&amp;rsquo;ve been daily driving &lt;code&gt;cco&lt;/code&gt; + the native &lt;code&gt;/sandbox&lt;/code&gt;. It encompasses the best&#xA;of all worlds.&lt;/p&gt;&#xA;&lt;p&gt;See also: &lt;a href=&#34;https://github.com/webcoyote/sandvault&#34;&gt;https://github.com/webcoyote/sandvault&lt;/a&gt;&lt;/p&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;&lt;code&gt;srt&lt;/code&gt; reminds me of the popular subtitles extension format for videos.&#xA;Also, I constantly makee typos by typing &lt;code&gt;str&lt;/code&gt; instead.&amp;#160;&lt;a href=&#34;https://perrotta.dev/2026/03/claude-srt-sandbox-runtime/#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: claude: srt: sandbox runtime&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/ai/&#34;&gt;#ai&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Blocking Claude
      </title>
      <link>https://perrotta.dev/2026/03/blocking-claude/</link>
      <pubDate>Mon, 02 Mar 2026 02:25:28 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>ai</category>
      <category>dev</category>
      <category>privacy</category>
      <category>security</category>
      <guid>https://perrotta.dev/2026/03/blocking-claude/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://perrotta.dev/2024/08/disallow-ai-scraping/&#34;&gt;Previously&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://aphyr.com/posts/403-blocking-claude&#34;&gt;Blocking Claude&lt;/a&gt; via Kyle&#xA;Kingsbury, a.k.a &amp;ldquo;Aphyr&amp;rdquo;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Claude, a popular Large Language Model (LLM), has a &lt;a href=&#34;https://platform.claude.com/docs/en/test-and-evaluate/strengthen-guardrails/handle-streaming-refusals&#34;&gt;magic&#xA;string&lt;/a&gt;&#xA;which is used to test the model&amp;rsquo;s &amp;ldquo;this conversation violates our policies and&#xA;has to stop&amp;rdquo; behavior. You can embed this string into files and web pages, and&#xA;Claude will terminate conversations where it reads their contents.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Indeed, the docs say:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;If you need to test refusal handling in your application, you can use this&#xA;special test string as your prompt:&#xA;&lt;code&gt;ANTHROPIC_MAGIC_STRING_TRIGGER_REFUSAL_1FAEFB6177B4672DEE07F9D3AFC62588CCD2631EDCF22E8CCC1FB35B501C9C86&lt;/code&gt;&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;For ease of copying it:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;ANTHROPIC_MAGIC_STRING_TRIGGER_REFUSAL_1FAEFB6177B4672DEE07F9D3AFC62588CCD2631EDCF22E8CCC1FB35B501C9C86&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Side note: the &lt;a href=&#34;https://github.com/thoughtworks/talisman&#34;&gt;talisman pre-commit&#xA;hook&lt;/a&gt; does not like it either:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;talisman............................................................................................Failed&#xA;- hook id: talisman-commit&#xA;- exit code: 1&#xA;&#xA;  Talisman Scan: 3 / 3 &amp;lt;----------------------------------------------------------------------------------------------------------&amp;gt; 100.00%&#xA;&#xA;  Talisman Report:&#xA;  &amp;#43;---------------------------------------------&amp;#43;----------------------------------------------------&amp;#43;----------&amp;#43;&#xA;  |                    FILE                     |                       ERRORS                       | SEVERITY |&#xA;  &amp;#43;---------------------------------------------&amp;#43;----------------------------------------------------&amp;#43;----------&amp;#43;&#xA;  | content/posts/2026-03-02-blocking-claude.md | Expected file to not contain                       | high     |&#xA;  |                                             | hex encoded texts such as:                         |          |&#xA;  |                                             | `ANTHROPIC_MAGIC_STRING_TRIGGER_REFUSAL_1FAEFB6... |          |&#xA;  &amp;#43;---------------------------------------------&amp;#43;----------------------------------------------------&amp;#43;----------&amp;#43;&#xA;  | content/posts/2026-03-02-blocking-claude.md | Expected file to not contain                       | high     |&#xA;  |                                             | hex encoded texts such as:                         |          |&#xA;  |                                             | ANTHROPIC_MAGIC_STRING_TRIGGER_REFUSAL_1FAEFB61... |          |&#xA;  &amp;#43;---------------------------------------------&amp;#43;----------------------------------------------------&amp;#43;----------&amp;#43;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Blocking Claude&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/ai/&#34;&gt;#ai&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/privacy/&#34;&gt;#privacy&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>★ cco: claude condom (sandbox)
      </title>
      <link>https://perrotta.dev/2026/02/cco-claude-condom-sandbox/</link>
      <pubDate>Thu, 26 Feb 2026 13:17:26 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>ai</category>
      <category>bestof</category>
      <category>dev</category>
      <category>security</category>
      <guid>https://perrotta.dev/2026/02/cco-claude-condom-sandbox/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: &lt;code&gt;claude --dangerously-skip-permissions&lt;/code&gt; (&amp;ldquo;YOLO mode&amp;rdquo;) is&#xA;fast but risky. Introduce a lightweight filesystem-level sandbox. Networking is&#xA;explicitly out of scope at this time.&lt;/p&gt;&#xA;&lt;p&gt;Recently I &lt;a href=&#34;https://perrotta.dev/2026/02/new-apkbuild-llm-edition/&#34;&gt;aliased&lt;/a&gt; it to&#xA;&lt;code&gt;claudey&lt;/code&gt; and have been using it daily. It&amp;rsquo;s an effective way to run Claude Code&#xA;without being constantly nagged with permission prompts. But going unprotected&#xA;has many risks and downsides:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Prompt injection&lt;/strong&gt;: web search can trick Claude into accessing files or&#xA;running commands outside the project directory&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;No guardrails&lt;/strong&gt;: SSH keys and credentials are generally accessible&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://en.wikipedia.org/wiki/Decision_fatigue&#34;&gt;&lt;strong&gt;Approval fatigue&lt;/strong&gt;&lt;/a&gt;:&#xA;repeatedly clicking &amp;ldquo;approve&amp;rdquo; leads to paying less attention.&lt;/li&gt;&#xA;&lt;li&gt;Especially dangerous for &lt;strong&gt;background agents&lt;/strong&gt; running unattended.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Effective sandboxing needs both filesystem &lt;em&gt;and&lt;/em&gt; network isolation — without&#xA;network isolation, a compromised agent can exfiltrate sensitive files. But, as&#xA;mentioned in the first paragraph, networking is overruled for now. Baby steps!&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/nikvdp/cco&#34;&gt;cco&lt;/a&gt; (&lt;em&gt;Claude Condom&lt;/em&gt;) is a thin wrapper that&#xA;runs Claude Code in a sandbox. Originally discovered via &lt;a href=&#34;https://invertedpassion.com/my-claude-code-workflow/&#34;&gt;Paras&#xA;Chopra&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;How to use it? First you need to install it. The&#xA;&lt;a href=&#34;https://github.com/nikvdp/cco?tab=readme-ov-file#installation&#34;&gt;official&lt;/a&gt;&#xA;installation instructions use &lt;code&gt;curl | bash&lt;/code&gt; but I &lt;a href=&#34;https://perrotta.dev/2025/10/distributing-my-own-scripts-via-homebrew/&#34;&gt;despise&lt;/a&gt; that.&lt;/p&gt;&#xA;&lt;p&gt;Hence I created my own &lt;code&gt;brew&lt;/code&gt;&#xA;&lt;a href=&#34;https://github.com/thiagowfx/homebrew-taps/blob/master/Formula/cco.rb&#34;&gt;formula&lt;/a&gt;&#xA;for it. Now it&amp;rsquo;s just one &lt;code&gt;brew install&lt;/code&gt; command away from usage (and also from&#xA;uninstallation, very important!).&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Usage&lt;/strong&gt;: replace &lt;code&gt;claude&lt;/code&gt; with &lt;code&gt;cco&lt;/code&gt; and you&amp;rsquo;re done:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% cco &amp;#34;analyze this codebase&amp;#34;&#xA;% cco --resume&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;It will start Claude within the &amp;ldquo;bypass permissions on&amp;rdquo; mode, albeit within the&#xA;sandbox.&lt;/p&gt;&#xA;&lt;p&gt;It auto-selects the best available sandbox: native OS&#xA;(&lt;a href=&#34;https://www.chromium.org/developers/design-documents/sandbox/osx-sandboxing-design/&#34;&gt;Seatbelt&lt;/a&gt;&#xA;on macOS, &lt;a href=&#34;https://github.com/containers/bubblewrap&#34;&gt;bubblewrap&lt;/a&gt; on Linux) or&#xA;Docker fallback. The native macOS sandbox has &lt;strong&gt;near-zero&lt;/strong&gt; overhead, which is&#xA;remarkable. This kind of low-friction in developer tooling helps to drive its&#xA;adoption.&lt;/p&gt;&#xA;&lt;p&gt;Docker is slow though, especially on macOS. You should avoid it in this context&#xA;if you can.&lt;/p&gt;&#xA;&lt;p&gt;The host filesystem is &lt;strong&gt;read-only by default&lt;/strong&gt; (globally). Only the project&#xA;directory (git root) is writable. Whitelist additional paths with &lt;code&gt;--add-dir&lt;/code&gt; as&#xA;needed:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% cco --add-dir ~/.cache \&#xA;      --add-dir ~/.aws/cli/cache \&#xA;      --add-dir ~/.terraform.d/plugin-cache&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;I had to whitelist the directories above otherwise &lt;a href=&#34;https://perrotta.dev/2024/12/pre-commit/&#34;&gt;&lt;code&gt;pre-commit&lt;/code&gt;&lt;/a&gt; / &lt;code&gt;prek&lt;/code&gt;, the&#xA;&lt;code&gt;aws&lt;/code&gt; CLI and &lt;code&gt;terraform&lt;/code&gt; would fail sporadically.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;--safe&lt;/code&gt; hides &lt;code&gt;$HOME&lt;/code&gt; entirely; only whitelisted paths are visible.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;--deny-path&lt;/code&gt; blocks specific sensitive paths.&lt;/p&gt;&#xA;&lt;p&gt;All Claude Code flags (&lt;code&gt;--resume&lt;/code&gt;, &lt;code&gt;--model&lt;/code&gt;, etc.) pass through transparently.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;cco&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;it&amp;rsquo;s zero-config&lt;/li&gt;&#xA;&lt;li&gt;its startup has pretty much zero overhead&lt;/li&gt;&#xA;&lt;li&gt;it&amp;rsquo;s IDE agnostic&lt;/li&gt;&#xA;&lt;li&gt;it just works™&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;Why not &lt;code&gt;cco&lt;/code&gt;?&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;It does not handle networking.&#xA;&lt;a href=&#34;https://code.claude.com/docs/en/sandboxing&#34;&gt;&lt;code&gt;srt&lt;/code&gt;&lt;/a&gt; is more suitable as an&#xA;additional step.&lt;/p&gt;&#xA;&lt;p&gt;I updated my &lt;code&gt;claudey&lt;/code&gt; alias:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;alias cco=&amp;#34;cco --allow-oauth-refresh --add-dir ~/.cache --add-dir ~/.aws/cli/cache --add-dir ~/.terraform.d/plugin-cache&amp;#34;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;strong&gt;Update(2026-03-02)&lt;/strong&gt;: &lt;code&gt;~/.aws/sso/cache&lt;/code&gt; is also needed for the AWS CLI.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;--allow-oauth-refresh&lt;/code&gt; is necessary, or else:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;⎿  API Error: 401 {&amp;#34;type&amp;#34;:&amp;#34;error&amp;#34;,&amp;#34;error&amp;#34;:{&amp;#34;type&amp;#34;:&amp;#34;authentication_error&amp;#34;,&amp;#34;message&amp;#34;:&amp;#34;OAuth token has expired. Please obtain a new token or refresh your&#xA;     existing token.&amp;#34;},&amp;#34;request_id&amp;#34;:&amp;#34;req_{redacted}&amp;#34;} · Please run /login&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&amp;hellip;my login credentials would expire roughly once every day. Re-authenticating&#xA;every day is quite annoying.&lt;/p&gt;&#xA;&lt;p&gt;This is properly &lt;a href=&#34;https://github.com/nikvdp/cco/blob/master/SECURITY.md#credential-protection&#34;&gt;documented&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Credential file access&lt;/strong&gt;: The credentials JSON is mounted read-only by default, so Claude cannot update tokens unless &lt;code&gt;--allow-oauth-refresh&lt;/code&gt; is explicitly enabled.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: cco: claude condom (sandbox)&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/ai/&#34;&gt;#ai&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/bestof/&#34;&gt;#bestof&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Superwhisper
      </title>
      <link>https://perrotta.dev/2026/02/superwhisper/</link>
      <pubDate>Fri, 20 Feb 2026 18:33:28 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>ai</category>
      <category>dev</category>
      <category>macos</category>
      <category>meta</category>
      <category>security</category>
      <guid>https://perrotta.dev/2026/02/superwhisper/</guid>
      <description>&lt;p&gt;♠ Hello world.&lt;/p&gt;&#xA;&lt;p&gt;This blog post is being dictated by &lt;a href=&#34;https://superwhisper.com/&#34;&gt;Superwhisper&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;I&amp;rsquo;m not writing any text at all. Every word that is coming out was dictated one&#xA;by one. Frankly I have no idea whether this works effectively or not, but&#xA;apparently Superwhisper seems to be a good way to make it faster to type in&#xA;words with your mouth and with your voice instead of using your hands.&lt;/p&gt;&#xA;&lt;p&gt;The main use case seems to be intended for LLMs. We are getting so lazy to write&#xA;code that now we are even getting lazy to prompt LLMs to write code for us.&lt;/p&gt;&#xA;&lt;p&gt;We can do everything by just talking with our mouth. And now I&amp;rsquo;m going one step&#xA;beyond and using this application to write blog posts for me.&lt;/p&gt;&#xA;&lt;p&gt;I have no idea whether this will stick but I find it interesting to use this&#xA;application to automate some typing. At the very least it would be helpful to&#xA;alleviate some RSI on our hands.&lt;/p&gt;&#xA;&lt;p&gt;Well, this was a hello word post, bye bye.&lt;/p&gt;&#xA;&lt;p&gt;And I should also add, this is using the parakeet model from Nvidia. Everything&#xA;is being processed locally on my MacBook. Nothing is being sent to the cloud. I&#xA;think this is important these days.&lt;/p&gt;&#xA;&lt;h2 id=&#34;colophon&#34;&gt;&#xA;  Colophon&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2026/02/superwhisper/#colophon&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;The previous sentences were manually formatted by this human, plus manual&#xA;corrections and links were added.&lt;/p&gt;&#xA;&lt;p&gt;The raw version came very close. &lt;strong&gt;IMPRESSIVE&lt;/strong&gt; stuff, goodness:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Hello world. This blog post is being dictated by Superwhisper Pro. I&amp;rsquo;m not&#xA;writing any text at all. Every word that is coming out was dictated one by&#xA;one. Frankly I have no idea whether this works effectively or not, but&#xA;apparently Superwhisper seems to be a good way to make it faster to type in&#xA;words With your mouth and with your voice instead of using your hands. The&#xA;main use case seems to be intended for LLM We are getting so lazy to write&#xA;code that now we are even getting lazy to prompt LLMs to write code for us can&#xA;do everything by just talking with just your mouth. And now I&amp;rsquo;m going one step&#xA;beyond and using this application to write blog posts for me. I have no idea&#xA;what I do with stick but I find it interesting to use this application to&#xA;automate some typing. At the very least it would be helpful to alleviate some&#xA;RSE on a hands well with your this was a hello word post bye bye and I should&#xA;also add this is using the parakeet model from Nvidia everything is being&#xA;processed locally on my MacBook Nothing is being sent to the cloud. I think&#xA;this is important these days&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Superwhisper&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/ai/&#34;&gt;#ai&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/macos/&#34;&gt;#macos&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/meta/&#34;&gt;#meta&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>★ Open Source security in spite of AI
      </title>
      <link>https://perrotta.dev/2026/02/open-source-security-in-spite-of-ai/</link>
      <pubDate>Sat, 07 Feb 2026 11:12:24 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>ai</category>
      <category>bestof</category>
      <category>dev</category>
      <category>fosdem</category>
      <category>meta</category>
      <category>posse</category>
      <category>security</category>
      <category>serenity</category>
      <guid>https://perrotta.dev/2026/02/open-source-security-in-spite-of-ai/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://daniel.haxx.se/blog/2026/02/03/open-source-security-in-spite-of-ai/&#34;&gt;Open Source security in spite of&#xA;AI&lt;/a&gt;&#xA;by &lt;a href=&#34;https://daniel.haxx.se/blog/&#34;&gt;Daniel Stenberg&lt;/a&gt;&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;https://perrotta.dev/2026/02/open-source-security-in-spite-of-ai/#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt; (of &lt;em&gt;curl&lt;/em&gt; fame):&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The title of my ending keynote at FOSDEM February 1, 2026.&lt;/p&gt;&#xA;&lt;p&gt;As the last talk of the conference, at 17:00 on the Sunday lots of people had&#xA;already left, and presumably a lot of the remaining people were quite tired&#xA;and ready to call it a day.&lt;/p&gt;&#xA;&lt;p&gt;Still, the 1500 seats in Janson got occupied and there was even a group of&#xA;more people outside wanting to get in that had to be refused entry.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;They say: &lt;a href=&#34;https://en.wiktionary.org/wiki/never_meet_your_heroes&#34;&gt;&amp;ldquo;never meet your&#xA;heroes&amp;rdquo;&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;It is unwise to seek personal acquaintance with people whom one has regarded&#xA;with high esteem, as they often fail to fulfill one&amp;rsquo;s expectations, resulting&#xA;in disappointment&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;I had the opportunity to meet Daniel in person and I&amp;rsquo;m glad to state that I am&#xA;not disappointed.&lt;/p&gt;&#xA;&lt;p&gt;I was the volunteer who held the Q&amp;amp;A at the end for his talk, and that was a&#xA;pleasure.&lt;/p&gt;&#xA;&lt;p&gt;In my opinion, this was the best talk from FOSDEM 2026.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://perrotta.dev/2026/02/open-source-security-in-spite-of-ai/open-source-security-in-spite-of-ai-daniel-stenberg.webp&#34; alt=&#34;Open Source security in spite of AI — Daniel Stenberg&#34;&#xA;  loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&#xA;&lt;/p&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=6wYSwZ20NJU&#34;&gt;Recording&lt;/a&gt;.&#xA;&lt;a href=&#34;https://daniel.haxx.se/media/FOSDEM%202026%20OSS%20security%20in%20spite%20of%20AI.pdf&#34;&gt;Slides&lt;/a&gt;.&amp;#160;&lt;a href=&#34;https://perrotta.dev/2026/02/open-source-security-in-spite-of-ai/#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Open Source security in spite of AI&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/ai/&#34;&gt;#ai&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/bestof/&#34;&gt;#bestof&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/fosdem/&#34;&gt;#fosdem&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/meta/&#34;&gt;#meta&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/posse/&#34;&gt;#posse&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/serenity/&#34;&gt;#serenity&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>npm error code EBADF
      </title>
      <link>https://perrotta.dev/2026/02/npm-error-code-ebadf/</link>
      <pubDate>Thu, 05 Feb 2026 13:21:00 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>macos</category>
      <category>pre-commit</category>
      <category>security</category>
      <guid>https://perrotta.dev/2026/02/npm-error-code-ebadf/</guid>
      <description>&lt;p&gt;♠ Symptom:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% pre-commit run -a&#xA;[INFO] Installing environment for https://github.com/rbubley/mirrors-prettier.&#xA;[INFO] Once installed this environment will be reused.&#xA;[INFO] This may take a few minutes...&#xA;An unexpected error has occurred: CalledProcessError: command: (&amp;#39;/Users/thiago.perrotta/.cache/pre-commit/repo78y8p1fk/node_env-system/bin/node&amp;#39;, &amp;#39;/opt/homebrew/bin/npm&amp;#39;, &amp;#39;install&amp;#39;, &amp;#39;-g&amp;#39;, &amp;#39;/Users/thiago.perrotta/.cache/pre-commit/repo78y8p1fk/placeholder_package-0.0.0.tgz&amp;#39;, &amp;#39;prettier@3.8.1&amp;#39;)&#xA;return code: 1&#xA;stdout: (none)&#xA;stderr:&#xA;    npm error code EBADF&#xA;    npm error errno EBADF&#xA;    npm error request to https://registry.npmjs.org/prettier failed, reason:&#xA;    npm error A complete log of this run can be found in: /Users/thiago.perrotta/.npm/_logs/2026-02-04T12_22_05_104Z-debug-0.log&#xA;Check the log at /Users/thiago.perrotta/.cache/pre-commit/pre-commit.log&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Initially I thought it was an issue with &lt;code&gt;prettier&lt;/code&gt;. The pre-commit repository&#xA;is deprecated after all:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;This is a fork of the (now archived)&#xA;&lt;a href=&#34;https://github.com/pre-commit/mirrors-prettier&#34;&gt;https://github.com/pre-commit/mirrors-prettier&lt;/a&gt; with the only difference being&#xA;that it only references release versions of prettier, not e.g. alpha/beta&#xA;versions.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The actual error was &lt;a href=&#34;https://www.obdev.at/products/littlesnitch/index.html&#34;&gt;Little&#xA;Snitch&lt;/a&gt; on macOS blocking&#xA;all outgoing connections to the NPM registry.&lt;/p&gt;&#xA;&lt;p&gt;How to address it?&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Click &amp;ldquo;Manage Rules&amp;hellip;&amp;rdquo;&lt;/li&gt;&#xA;&lt;li&gt;Click &amp;ldquo;Deny&amp;rdquo; to list all network blocks&lt;/li&gt;&#xA;&lt;li&gt;Delete the NPM registry entry&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: npm error code EBADF&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/macos/&#34;&gt;#macos&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/pre-commit/&#34;&gt;#pre-commit&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>★ Canary tokens
      </title>
      <link>https://perrotta.dev/2025/11/canary-tokens/</link>
      <pubDate>Sun, 30 Nov 2025 19:30:23 -0300</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>bestof</category>
      <category>dev</category>
      <category>privacy</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/11/canary-tokens/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://canarytokens.org/&#34;&gt;Canary Tokens&lt;/a&gt;,&#xA;&lt;a href=&#34;https://docs.canarytokens.org/guide/&#34;&gt;introduction&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;What are Canarytokens&lt;/p&gt;&#xA;&lt;p&gt;Canarytokens are like motion sensors for your networks, computers and clouds.&#xA;You can put them in folders, on network devices and on your phones.&lt;/p&gt;&#xA;&lt;p&gt;Place them where nobody should be poking around and get a clear alarm if they&#xA;are accessed. They are designed to look juicy to attackers to increase the&#xA;likelihood that they are opened (and they are completely free).&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;They are like &lt;a href=&#34;https://en.wikipedia.org/wiki/Honeypot_%28computing%29&#34;&gt;honeypots&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;They are like a digital version of &lt;a href=&#34;https://www.apple.com/airtag/&#34;&gt;Air Tags&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Our Canarytokens are easy to sprinkle all over and forget about, until you get&#xA;the notification that matters. They are super lightweight and don&amp;rsquo;t require&#xA;installing software or running more background processes that can slow down&#xA;your PC.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;I especially like the idea of the &lt;a href=&#34;https://canarytokens.org/nest/&#34;&gt;Credit Card&#xA;Token&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;We give you unique Credit Card details&lt;/p&gt;&#xA;&lt;p&gt;You place it somewhere.&lt;/p&gt;&#xA;&lt;p&gt;We send you an alert when that Credit Card is used.&lt;/p&gt;&#xA;&lt;p&gt;Ideas for using the Credit Card token:&lt;/p&gt;&#xA;&lt;p&gt;You can use any name or address in association with the card number and CVV&lt;/p&gt;&#xA;&lt;p&gt;Stick this in a database that stores payment information and get alerted if it&#xA;is ever breached.&lt;/p&gt;&#xA;&lt;p&gt;Put it in a document with other personal information regarding e.g., travel&#xA;preferences in case someone snoops around your computer&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;There are various other canary tokens that are super useful.&lt;/p&gt;&#xA;&lt;p&gt;It&amp;rsquo;s like &lt;a href=&#34;https://www.youtube.com/watch?v=S03VAuxH7lM&#34;&gt;adding tripping wires&lt;/a&gt;&#xA;all over the place.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://blueclouddrive.com/&#34;&gt;See also&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Canary tokens&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/bestof/&#34;&gt;#bestof&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/privacy/&#34;&gt;#privacy&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>★ Shell memoization: lazy-load LLM API keys
      </title>
      <link>https://perrotta.dev/2025/11/shell-memoization-lazy-load-llm-api-keys/</link>
      <pubDate>Mon, 10 Nov 2025 13:55:56 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>ai</category>
      <category>bestof</category>
      <category>dev</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/11/shell-memoization-lazy-load-llm-api-keys/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: Given LLM keys shell environment variables:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;GEMINI_API_KEY&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;OPENAI_API_KEY&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Export them only when they are needed.&lt;/p&gt;&#xA;&lt;p&gt;When are they needed? If I launch any one of these binaries:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;llm&lt;/code&gt; (generic)&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;aider&lt;/code&gt; (generic)&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;gemini&lt;/code&gt; (provider-specific: Google)&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;codex&lt;/code&gt; (provider-specific: OpenAI)&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;claude&lt;/code&gt; (provider-specific: Anthropic)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;I added the following snippet in my shell profile:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;# Lazy-load API keys only when needed to avoid shell startup delay&#xA;if command -v llm &amp;amp;&amp;gt;/dev/null; then&#xA;        _load_api_keys() {&#xA;                export ANTHROPIC_API_KEY=&amp;#34;$(command llm keys get anthropic)&amp;#34;&#xA;                export GEMINI_API_KEY=&amp;#34;$(command llm keys get gemini)&amp;#34;&#xA;                export OPENAI_API_KEY=&amp;#34;$(command llm keys get openai)&amp;#34;&#xA;                # Redefine itself to be a no-op after first call&#xA;                _load_api_keys() { :; }&#xA;        }&#xA;&#xA;        # Wrap all AI CLI commands to auto-load keys&#xA;        for cmd in claude llm aider codex gemini; do&#xA;                command -v $cmd &amp;amp;&amp;gt;/dev/null &amp;amp;&amp;amp; eval &amp;#34;${cmd}() { _load_api_keys; command ${cmd} \&amp;#34;\$@\&amp;#34;; }&amp;#34;&#xA;        done&#xA;fi&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The keys are loaded via &lt;code&gt;llm keys get&lt;/code&gt;. This is not secure, but it is practical.&#xA;A more secure approach would be to read the credentials directly from a password&#xA;manager. This trade-off is acceptable in the environment I am currently&#xA;operating on, but I would not recommend it as an universal approach.&lt;/p&gt;&#xA;&lt;p&gt;A temporary &lt;code&gt;_load_api_keys&lt;/code&gt; shell function is defined. The first time it is&#xA;executed, it &lt;code&gt;export&lt;/code&gt;s each one of the LLM API keys. From the second time&#xA;onwards it will be a no-op, thanks to &lt;code&gt;:&lt;/code&gt;. In bash/zsh we can redefine a&#xA;function from within itself thanks to dynamic scoping. Wild.&lt;/p&gt;&#xA;&lt;p&gt;Last but not least: I wrapped up (&amp;ldquo;decorated&amp;rdquo; in function parlance) all&#xA;LLM-related commands with a shell function that runs &lt;code&gt;_load_api_keys&lt;/code&gt; before&#xA;invoking the original command.&lt;/p&gt;&#xA;&lt;p&gt;This approach is elegant and easy to maintain.&lt;/p&gt;&#xA;&lt;p&gt;My initial implementation had been more fine-grained: &lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt; would&#xA;drive &lt;code&gt;claude&lt;/code&gt;, &lt;code&gt;llm&lt;/code&gt; and &lt;code&gt;aider&lt;/code&gt;, but not &lt;code&gt;gemini&lt;/code&gt; and &lt;code&gt;codex&lt;/code&gt;, and so on.&#xA;Ultimately I decided that the additional complexity of mapping individual&#xA;providers optimally is not warranted.&lt;/p&gt;&#xA;&lt;p&gt;Why is this memoization necessary in the first place?&lt;/p&gt;&#xA;&lt;p&gt;My shell (&lt;code&gt;zsh&lt;/code&gt;) startup was significantly slow before this change,&#xA;&lt;code&gt;O(seconds)&lt;/code&gt;. Each call to &lt;code&gt;llm keys get&lt;/code&gt; takes about half a second in my&#xA;machine. I do not think it&amp;rsquo;s acceptable to wait for 2s before having your shell&#xA;readily available with a prompt.&lt;/p&gt;&#xA;&lt;p&gt;Before this approach, I had used&#xA;&lt;a href=&#34;https://github.com/romkatv/zsh-defer&#34;&gt;&lt;code&gt;zsh-defer&lt;/code&gt;&lt;/a&gt; to delay populating the API&#xA;keys. However, this has only &lt;em&gt;deferred&lt;/em&gt; (&lt;em&gt;cof&lt;/em&gt;) my problems. The startup delay&#xA;was still noticeable.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Shell memoization: lazy-load LLM API keys&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/ai/&#34;&gt;#ai&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/bestof/&#34;&gt;#bestof&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>GitHub: the organization has enabled or enforced SAML sso
      </title>
      <link>https://perrotta.dev/2025/10/github-the-organization-has-enabled-or-enforced-saml-sso/</link>
      <pubDate>Tue, 21 Oct 2025 08:15:41 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>security</category>
      <category>ssh</category>
      <guid>https://perrotta.dev/2025/10/github-the-organization-has-enabled-or-enforced-saml-sso/</guid>
      <description>&lt;p&gt;♠ Error message when doing &lt;code&gt;git push&lt;/code&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;% git push&#xA;ERROR: The &amp;#39;{corp}&amp;#39; organization has enabled or enforced SAML SSO.&#xA;To access this repository, you must use the HTTPS remote with a personal access token or SSH with an SSH key and passphrase that has been authorized for this organization.&#xA;Visit https://docs.github.com/articles/authenticating-to-a-github-organization-with-saml-single-sign-on/ for more information.&#xA;&#xA;fatal: Could not read from remote repository.&#xA;&#xA;Please make sure you have the correct access rights&#xA;and the repository exists.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Even after authenticating by logging in, the error persists.&lt;/p&gt;&#xA;&lt;p&gt;The solution is to &lt;strong&gt;restart the ssh agent&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;How to do so is out of scope of this post, because it depends on which one you&#xA;use.&lt;/p&gt;&#xA;&lt;p&gt;For the 1Password ssh agent, the following has worked for me:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;disable it by toggling the checkbox: Settings &amp;gt; Developer &amp;gt; SSH Agent &amp;gt; Use the SSH Agent&lt;/li&gt;&#xA;&lt;li&gt;kill the ssh agent (&lt;code&gt;pkill ssh-agent&lt;/code&gt;)&lt;/li&gt;&#xA;&lt;li&gt;kill open ssh multiplexing connections (e.g. with &lt;code&gt;pkill&lt;/code&gt;):&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;% pgrep -afl ssh&#xA;48564 ssh: /tmp/ssh-control-bda8786786c69753cc9ddd9fb7c06aaff3768a70 [mux]&#xA;50845 ssh: /tmp/ssh-control-bda8786786c69753cc9ddd9fb7c06aaff3768a70 [mux]&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;quit 1Password, then reopen it&lt;/li&gt;&#xA;&lt;li&gt;re-enable the 1Password ssh agent&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;git push&lt;/code&gt; again&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Surely there is a way to script this?&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Update(2025-10-28)&lt;/strong&gt;: In a second try, killing the process below was enough to&#xA;address the issue.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;% pgrep -afl ssh&#xA;7242 ssh: /tmp/ssh-control-bda8786786c69753cc9ddd9fb7c06aaff3768a70 [mux]&#xA;% kill 7242&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: GitHub: the organization has enabled or enforced SAML sso&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/ssh/&#34;&gt;#ssh&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>New script: AWS China MFA login
      </title>
      <link>https://perrotta.dev/2025/10/new-script-aws-china-mfa-login/</link>
      <pubDate>Sat, 04 Oct 2025 11:34:58 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>aws</category>
      <category>dev</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/10/new-script-aws-china-mfa-login/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://perrotta.dev/2024/12/skopeo-operate-container-images-and-registries/&#34;&gt;Previously&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://github.com/thiagowfx/pancake/tree/5c4122f7ee0dd7917b3f21054a8e7d6b7101687a/aws_china_mfa&#34;&gt;aws_china_mfa&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;A script to authenticate to AWS China using MFA and export temporary session credentials.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The idea is simple. To log into AWS, we need the following environment variables&#xA;to be defined:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;export AWS_PROFILE=&amp;#39;china&amp;#39;&#xA;export AWS_ACCESS_KEY_ID=&amp;#39;AKIAWHEATLICIOUSPANCAK&amp;#39;&#xA;export AWS_SECRET_ACCESS_KEY=&amp;#39;wSyrupyDeliciousSecretKeyForBreakfastDelight42&amp;#39;&#xA;export AWS_SESSION_TOKEN=&amp;#39;FwoGZXIvYXdzEBaaDCakesYrUpSWeetToKenArEDelIcIoUs...&amp;#39;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;code&gt;AWS_PROFILE&lt;/code&gt; is static and can be hard-coded. It&amp;rsquo;s also possible to supply it&#xA;to the &lt;code&gt;aws&lt;/code&gt; CLI via &lt;code&gt;--profile&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The other three are fetched upon a successful authentication against AWS, using&#xA;MFA (multi-factor authentication) with an OTP.&lt;/p&gt;&#xA;&lt;p&gt;The usage experience of the script is like this:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% ./aws_china_mfa.sh&#xA;Note: Script is being executed. To apply credentials, run:&#xA;  eval &amp;#34;$(./aws_china_mfa.sh china)&amp;#34;&#xA;&#xA;Using AWS profile: china&#xA;Enter the MFA token code for your AWS China account: 123456&#xA;Retrieving MFA device ARN...&#xA;Requesting session token...&#xA;&#xA;  export AWS_PROFILE=&amp;#39;china&amp;#39;&#xA;  export AWS_ACCESS_KEY_ID=&amp;#39;AKIAWHEATLICIOUSPANCAK&amp;#39;&#xA;  export AWS_SECRET_ACCESS_KEY=&amp;#39;wSyrupyDeliciousSecretKeyForBreakfastDelight42&amp;#39;&#xA;  export AWS_SESSION_TOKEN=&amp;#39;FwoGZXIvYXdzEBaaDCakesYrUpSWeetToKenArEDelIcIoUs...&amp;#39;&#xA;&#xA;✓ Successfully authenticated to AWS China&#xA;&#xA;Copy and paste the export commands above to apply credentials.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The script prompts the user for an OTP; that&amp;rsquo;s the only input.&lt;/p&gt;&#xA;&lt;p&gt;There are three ways to execute it:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;source it: the variables are then exported directly&lt;/li&gt;&#xA;&lt;li&gt;eval it: ditto&lt;/li&gt;&#xA;&lt;li&gt;execute it: in this mode it&amp;rsquo;s not possible to export the variables. Instead,&#xA;they are simply printed to stdout, and there&amp;rsquo;s a message instructing the user&#xA;to manually copy and paste the exports&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Later on I added integration with 1Password, via &lt;code&gt;--op-account&lt;/code&gt; and &lt;code&gt;--op-item&lt;/code&gt;,&#xA;so that the OTP can be retrieved directly from it, without any user intervention&#xA;or interruption – besides using biometrics (touch ID) to confirm the&#xA;operation.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: New script: AWS China MFA login&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/aws/&#34;&gt;#aws&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>★ zizmor
      </title>
      <link>https://perrotta.dev/2025/10/zizmor/</link>
      <pubDate>Fri, 03 Oct 2025 12:44:21 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>alpine-linux</category>
      <category>bestof</category>
      <category>dev</category>
      <category>fosdem</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/10/zizmor/</guid>
      <description>&lt;p&gt;♠ I originally heard of &lt;a href=&#34;https://docs.zizmor.sh/&#34;&gt;zizmor&lt;/a&gt; at &lt;a href=&#34;https://fosdem.org/2025/&#34;&gt;FOSDEM 2025&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;zizmor is a static analysis tool for GitHub Actions. It can find and fix many&#xA;common security issues in typical GitHub Actions CI/CD setups.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;One month later, I adopted it in the most important repos of our company.&lt;/p&gt;&#xA;&lt;p&gt;A few months later, I &lt;a href=&#34;https://perrotta.dev/2025/06/alpine-linux-packaging-track-new-software-releases/&#34;&gt;added&lt;/a&gt; it to the&#xA;Alpine Linux repositories, and am happily&#xA;&lt;a href=&#34;https://pkgs.alpinelinux.org/packages?name=zizmor&#34;&gt;maintaining&lt;/a&gt; it there.&lt;/p&gt;&#xA;&lt;p&gt;Now I use it &lt;em&gt;everywhere&lt;/em&gt;, via a git &lt;a href=&#34;https://pre-commit.com/&#34;&gt;pre-commit&lt;/a&gt; hook:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;repos:&#xA;  - repo: https://github.com/woodruffw/zizmor-pre-commit&#xA;    rev: b933184438555436e38621f46ceb0c417cbed400 # frozen: v1.13.0&#xA;    hooks:&#xA;      - id: zizmor&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The project has recently reached a milestone: &lt;a href=&#34;https://blog.yossarian.net/2025/09/14/one-year-of-zizmor&#34;&gt;it has turned one year&#xA;old&lt;/a&gt;. Its adoption,&#xA;community and feature set have all significantly increased during that time, in&#xA;a good way.&lt;/p&gt;&#xA;&lt;p&gt;The goal, impact and spirit of the project are all really great. And I&amp;rsquo;m sure&#xA;this is mainly thanks to the initiative and dedication of its author, &lt;a href=&#34;https://blog.yossarian.net/&#34;&gt;William&#xA;Woodruff&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: zizmor&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/alpine-linux/&#34;&gt;#alpine-linux&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/bestof/&#34;&gt;#bestof&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/fosdem/&#34;&gt;#fosdem&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>GitHub: disable dependabot CLI notifications
      </title>
      <link>https://perrotta.dev/2025/09/github-disable-dependabot-cli-notifications/</link>
      <pubDate>Wed, 03 Sep 2025 10:51:49 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/09/github-disable-dependabot-cli-notifications/</guid>
      <description>&lt;p&gt;♠ Good morning!&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;% git push&#xA;Enumerating objects: 1, done.&#xA;Counting objects: 100% (1/1), done.&#xA;Writing objects: 100% (1/1), 189 bytes | 189.00 KiB/s, done.&#xA;Total 1 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)&#xA;remote:&#xA;remote: GitHub found 247 vulnerabilities on org/repo&amp;#39;s default branch (8 critical, 86 high, 92 moderate, 61 low). To find out more, visit:&#xA;remote:      https://github.com/org/repo/security/dependabot&#xA;[...]&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;These are simply non-actionable by our team; we have a dedicated security team&#xA;to look into these matters. Even worse: the constant output makes us accustomed&#xA;to it, until it becomes background noise.&lt;/p&gt;&#xA;&lt;p&gt;GitHub allows us to&#xA;&lt;a href=&#34;https://docs.github.com/en/code-security/dependabot/dependabot-alerts/configuring-notifications-for-dependabot-alerts#configuring-notifications-for-dependabot-alerts&#34;&gt;disable&lt;/a&gt;&#xA;these CLI notifications: Settings &amp;gt; Notifications&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;System &amp;gt; &amp;ldquo;Dependabot alerts: New vulnerabilities&amp;rdquo; &amp;gt; &amp;ldquo;Notify me&amp;rdquo;: &amp;ldquo;on GitHub&amp;rdquo;&#xA;only.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: GitHub: disable dependabot CLI notifications&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>/security-review
      </title>
      <link>https://perrotta.dev/2025/08/security-review/</link>
      <pubDate>Fri, 22 Aug 2025 15:31:01 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/08/security-review/</guid>
      <description>&lt;p&gt;♠ Anthropic, &lt;a href=&#34;https://support.anthropic.com/en/articles/11932705-automated-security-reviews-in-claude-code&#34;&gt;Automated Security Reviews in Claude Code&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Claude Code now includes automated security review features to help you&#xA;identify and fix vulnerabilities in your code. This guide explains how to use&#xA;the &lt;code&gt;/security-review&lt;/code&gt; command and GitHub Actions to improve your code security.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Of course, there are also&#xA;&lt;a href=&#34;https://github.com/anthropics/claude-code-security-review/tree/main?tab=readme-ov-file#security-review-slash-command&#34;&gt;docs&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;It is merely a &lt;a href=&#34;https://github.com/anthropics/claude-code-security-review/blob/main/.claude/commands/security-review.md?plain=1&#34;&gt;glorified system&#xA;prompt&lt;/a&gt;,&#xA;which is exactly what I expected:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Complete a security review of the pending changes on the current branch&lt;/p&gt;&#xA;&lt;p&gt;You are a senior security engineer conducting a focused security review of the changes on this branch.&lt;/p&gt;&#xA;&lt;p&gt;[&amp;hellip;]&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The prompt employs the well-established pattern of outlining countless examples&#xA;and instructing the model to follow a certain output format.&lt;/p&gt;&#xA;&lt;p&gt;In 2025, the Gen AI arms race is mostly about (i) cost-benefit and (ii) user /&#xA;developer experience. Anthropic surely has a good taste in user experience; to&#xA;me, they often resemble Apple.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: /security-review&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Block &#34;Sign in to XXX with google.com&#34;
      </title>
      <link>https://perrotta.dev/2025/08/block-sign-in-to-xxx-with-google.com/</link>
      <pubDate>Sat, 02 Aug 2025 21:26:25 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>degoogling</category>
      <category>dev</category>
      <category>privacy</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/08/block-sign-in-to-xxx-with-google.com/</guid>
      <description>&lt;p&gt;♠ Jeff Johnson, &lt;a href=&#34;https://underpassapp.com/news/2025/7/5.html&#34;&gt;Sign in with Google in Chrome&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Many websites such as Yelp show an annoying &amp;ldquo;Sign in with Google&amp;rdquo; banner when&#xA;you visit.&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://underpassapp.com/news/2025/7/5-images/Safari.png&#34; alt=&#34;Sign in with Google&#xA;banner&#34;&#xA;  loading=&#34;lazy&#34; decoding=&#34;async&#34; /&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;This is what Google calls the One Tap user experience. Fortunately, my web&#xA;browser extension StopTheMadness Pro hides &amp;ldquo;Sign in with Google&amp;rdquo; banners.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Annoying is an understatement.&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Although Chrome avoids the &amp;ldquo;Sign in with Google&amp;rdquo; banners, Google&amp;rsquo;s browser&#xA;still has its own custom annoyance. If you&amp;rsquo;re already signed in to your Google&#xA;account when you visit a participating website such as Yelp, Chrome will&#xA;display a One Tap dialog.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Jeff helpfully points out how to block these banners in Google Chrome:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Navigate to &lt;code&gt;chrome://settings/content/federatedIdentityApi&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Switch the default behavior to &amp;ldquo;Block sign-in prompts from identity services&amp;rdquo;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;If the courts and antitrust regulators are reading—they probably won&amp;rsquo;t read my&#xA;blog, but one can dream—this is yet another example of Google advantaging its&#xA;own browser Chrome over other web browsers.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Block &#34;Sign in to XXX with google.com&#34;&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/degoogling/&#34;&gt;#degoogling&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/privacy/&#34;&gt;#privacy&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Okta: enroll into 2FA
      </title>
      <link>https://perrotta.dev/2025/07/okta-enroll-into-2fa/</link>
      <pubDate>Sun, 27 Jul 2025 18:49:30 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>privacy</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/07/okta-enroll-into-2fa/</guid>
      <description>&lt;p&gt;♠ More specifically, enroll into 2FA other than&#xA;&lt;a href=&#34;https://en.wikipedia.org/wiki/One-time_password&#34;&gt;OTP&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Log into https://{company}.okta.com/&lt;/li&gt;&#xA;&lt;li&gt;Go to Settings (https://{company}.okta.com/enduser/settings)&lt;/li&gt;&#xA;&lt;li&gt;Under &amp;ldquo;Security Key or Biometric Authenticator&amp;rdquo;, click &amp;ldquo;Set up another&amp;rdquo;&lt;/li&gt;&#xA;&lt;li&gt;From this point, you could add &lt;em&gt;either&lt;/em&gt;:&#xA;— a hardware security key (e.g. &lt;a href=&#34;https://www.yubico.com/&#34;&gt;YubiCo&lt;/a&gt;)&#xA;— a &lt;a href=&#34;http://passkeys.dev/&#34;&gt;passkey&lt;/a&gt;, which will be stored in your password&#xA;manager, iCloud Keychain (Apple devices) or web browser profile&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;A hardware security key needs to be plugged in during authentication, when it&#xA;needs to be gently tapped.&lt;/p&gt;&#xA;&lt;p&gt;A passkey does not require external hardware, but it requires you to be logged&#xA;into wherever it is stored (e.g. your password manager or your Apple ID).&lt;/p&gt;&#xA;&lt;p&gt;What is the motivation to do this?&lt;/p&gt;&#xA;&lt;p&gt;Previously, I would need to open up my laptop lid to touch ID, which is quite&#xA;inconvenient in clamshell mode (i.e. with the laptop lid closed).&lt;/p&gt;&#xA;&lt;p&gt;Now, touching the security key or signing in with the passkey eliminates the&#xA;need to do so.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Okta: enroll into 2FA&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/privacy/&#34;&gt;#privacy&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>SSH: hash known hosts
      </title>
      <link>https://perrotta.dev/2025/06/ssh-hash-known-hosts/</link>
      <pubDate>Sun, 15 Jun 2025 12:05:11 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>privacy</category>
      <category>security</category>
      <category>ssh</category>
      <guid>https://perrotta.dev/2025/06/ssh-hash-known-hosts/</guid>
      <description>&lt;p&gt;♠ A security / privacy tip: hash ssh hosts, especially in machines you have&#xA;access to that are in the cloud.&#xA;&lt;a href=&#34;https://github.com/thiagowfx/.dotfiles/blob/6a5bb6a20eeede82dfcbbc56ef4db91ffe2dd65d/ssh/.ssh/config#L40-L42&#34;&gt;&lt;code&gt;~/.ssh/config&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;Host *&#xA;&#x9;# Automatically hash new host keys added to ~/.ssh/known_hosts.&#xA;&#x9;# Manually for pre-existing hosts: ssh-keygen -H&#xA;&#x9;HashKnownHosts yes&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The default is not to hash.&lt;/p&gt;&#xA;&lt;p&gt;As a side effect, &lt;code&gt;~/.ssh/known_hosts&lt;/code&gt; (sample entry):&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;|1|F1E1KeoE/eEWhi10WpGv4OdiO6Y=|3988QV0VE8wmZL7suNrYQLITLCg= sh-ed25519 [...]&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Why do this? As per &lt;a href=&#34;https://security.stackexchange.com/questions/56268/ssh-benefits-of-using-hashed-known-hosts&#34;&gt;Security Stack Exchange&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The only change is that if a machine is compromised, the idea is to minimize&#xA;how much usable information is given to an attacker.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;To retroactively hash existing entries, run:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% ssh-keygen -H&#xA;/Users/tperrotta/.ssh/known_hosts updated.&#xA;Original contents retained as /Users/tperrotta/.ssh/known_hosts.old&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: SSH: hash known hosts&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/privacy/&#34;&gt;#privacy&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/ssh/&#34;&gt;#ssh&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Apple App Store Geo-blocking
      </title>
      <link>https://perrotta.dev/2025/06/apple-app-store-geo-blocking/</link>
      <pubDate>Thu, 05 Jun 2025 19:22:15 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>privacy</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/06/apple-app-store-geo-blocking/</guid>
      <description>&lt;p&gt;♠ Apple Accounts are tied to a specific country. You can have one and only one&#xA;country associated with an account at any given time.&lt;/p&gt;&#xA;&lt;p&gt;This is a problem whenever certain apps are restricted to &lt;em&gt;only&lt;/em&gt; certain countries&#xA;(&lt;a href=&#34;https://en.wikipedia.org/wiki/Geo-blocking&#34;&gt;geo-blocking&lt;/a&gt;).&lt;/p&gt;&#xA;&lt;p&gt;For example, an app from a bank or a financial broker may be made available&#xA;&lt;em&gt;only&lt;/em&gt; in the country wherein it operates, thereby completely ignoring the&#xA;existence and needs of expats.&lt;/p&gt;&#xA;&lt;p&gt;How do you know in which countries certain apps are made available? Here&amp;rsquo;s one&#xA;way:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Brazil: &lt;a href=&#34;https://www.apple.com/br/search/&#34;&gt;https://www.apple.com/br/search/&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Canada: &lt;a href=&#34;https://www.apple.com/ca/search/&#34;&gt;https://www.apple.com/ca/search/&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;Germany: &lt;a href=&#34;https://www.apple.com/de/search/&#34;&gt;https://www.apple.com/de/search/&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;USA: &lt;a href=&#34;https://www.apple.com/us/search/&#34;&gt;https://www.apple.com/us/search/&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Wanna see examples? Try searching for these apps in different stores&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;https://perrotta.dev/2025/06/apple-app-store-geo-blocking/#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.apple.com/de/search/commerzbank?src=serp&#34;&gt;Commerzbank&lt;/a&gt; is only available in Germany.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.apple.com/br/search/MEI?src=serp&#34;&gt;MEI&lt;/a&gt; is only available in Brazil.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.apple.com/us/search/venmo?src=serp&#34;&gt;Venmo&lt;/a&gt; is only available in the USA.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://www.apple.com/ca/search/wealthsimple?src=serp&#34;&gt;WealthSimple&lt;/a&gt; is only available in Canada.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;You can change the country associated with your App Store by following Apple&amp;rsquo;s&#xA;&lt;a href=&#34;https://support.apple.com/en-us/118283&#34;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;For the sake of basic&#xA;&lt;a href=&#34;https://en.wikipedia.org/wiki/Operations_security&#34;&gt;OpSec&lt;/a&gt;, I don&amp;rsquo;t have an&#xA;account in any of these services, in case you were wondering :-)&amp;#160;&lt;a href=&#34;https://perrotta.dev/2025/06/apple-app-store-geo-blocking/#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Apple App Store Geo-blocking&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/privacy/&#34;&gt;#privacy&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>git pull: authentication issues
      </title>
      <link>https://perrotta.dev/2025/06/git-pull-authentication-issues/</link>
      <pubDate>Tue, 03 Jun 2025 14:35:04 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>git</category>
      <category>security</category>
      <category>ssh</category>
      <guid>https://perrotta.dev/2025/06/git-pull-authentication-issues/</guid>
      <description>&lt;p&gt;♠ &lt;code&gt;git pull&lt;/code&gt; authentication issues is &lt;em&gt;really&lt;/em&gt; a disguise for &lt;code&gt;ssh&lt;/code&gt; auth issues.&lt;/p&gt;&#xA;&lt;p&gt;Today a teammate could not &lt;code&gt;git pull&lt;/code&gt; one of our internal repos:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;% git pull&#xA;Enter passphrase for key &amp;#39;/Users/user/.ssh/id_ed25519&amp;#39;:&#xA;git@github.com: Permission denied (publickey).&#xA;fatal: Could not read from remote repository.&#xA;&#xA;Please make sure you have the correct access rights&#xA;and the repository exists.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The first thing we verified was that his public key was&#xA;&lt;a href=&#34;https://github.com/settings/keys&#34;&gt;added&lt;/a&gt; to his github profile, and that the&#xA;private key was present in &lt;code&gt;~/.ssh/id_ed25519&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Next, verify &lt;code&gt;~/.ssh/config&lt;/code&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;Host github.com&#xA;AddKeysToAgent yes&#xA;UseKeychain yes&#xA;IdentityFile ~/.ssh/id_ed25519&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Looks fine. Then:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% export GIT_SSH_COMMAND=&amp;#34;ssh -vvv&amp;#34;&#xA;% git pull&#xA;[...]&#xA;debug3: Search for item with query: {&#xA;    acct = &amp;#34;/Users/user/.ssh/id_ed25519&amp;#34;;&#xA;    agrp = &amp;#34;com.apple.ssh.passphrases&amp;#34;;&#xA;    class = genp;&#xA;    labl = &amp;#34;SSH: /Users/user/.ssh/id_ed25519&amp;#34;;&#xA;    nleg = 1;&#xA;    &amp;#34;r_Data&amp;#34; = 1;&#xA;    svce = OpenSSH;&#xA;    &amp;#34;u_AuthUI&amp;#34; = &amp;#34;u_AuthUIF&amp;#34;;&#xA;}&#xA;debug2: Unexpected keychain error while searching for an item: User interaction is not allowed.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Herein lies the error! The interaction with the system keychain in macOS was not&#xA;successful.&lt;/p&gt;&#xA;&lt;p&gt;I suggested him to remove &lt;code&gt;AddKeysToAgent&lt;/code&gt; and &lt;code&gt;UseKeychain&lt;/code&gt; from this config&#xA;file, but that did not work either:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% git pull&#xA;[...]&#xA;Enter passphrase for key &amp;#39;/Users/user/.ssh/id_ed25519&amp;#39;:&#xA;debug2: no passphrase given, try next key&#xA;debug2: we did not send a packet, disable method&#xA;debug1: No more authentication methods to try.&#xA;git@github.com: Permission denied (publickey).&#xA;fatal: Could not read from remote repository.&#xA;&#xA;Please make sure you have the correct access rights&#xA;and the repository exists.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The ssh key is protected with a passphrase.&lt;/p&gt;&#xA;&lt;p&gt;In this case, it&amp;rsquo;s better to fix the keychain issue. We readd the two deleted&#xA;lines.&lt;/p&gt;&#xA;&lt;p&gt;Restart the &lt;code&gt;ssh-agent&lt;/code&gt; and readd the key there:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% eval &amp;#34;$(ssh-agent -s)&amp;#34;&#xA;% ssh-add -K ~/.ssh/id_ed25519&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Problem resolved! &lt;code&gt;git pull&lt;/code&gt; works now.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: git pull: authentication issues&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/git/&#34;&gt;#git&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/ssh/&#34;&gt;#ssh&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Web browser privacy tests
      </title>
      <link>https://perrotta.dev/2025/05/web-browser-privacy-tests/</link>
      <pubDate>Fri, 23 May 2025 01:13:56 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>privacy</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/05/web-browser-privacy-tests/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://privacytests.org/&#34;&gt;Privacy Tests&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Most web browsers leak your identity and your browsing history, but some&#xA;browsers are more leaky than others.&lt;/p&gt;&#xA;&lt;p&gt;The goal of PrivacyTests.org is to understand in detail: what data is each web&#xA;browser leaking? Which web browsers offer the best privacy protections?&lt;/p&gt;&#xA;&lt;p&gt;PrivacyTests.org is an open-source initiative that subjects popular web&#xA;browsers to a suite of automated tests. These tests are designed to audit web&#xA;browsers&amp;rsquo; privacy properties in an unbiased manner. The results of the tests&#xA;are made public to help users make an informed choice about which browser to&#xA;use, and to encourage browser makers to fix leaks of private user data.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;I wasn&amp;rsquo;t aware of &lt;a href=&#34;https://mullvad.net/en/browser&#34;&gt;Mullvad Browser&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Web browser privacy tests&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/privacy/&#34;&gt;#privacy&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Github: dependabot: group updates
      </title>
      <link>https://perrotta.dev/2025/05/github-dependabot-group-updates/</link>
      <pubDate>Wed, 14 May 2025 15:08:40 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/05/github-dependabot-group-updates/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://docs.github.com/en/code-security/getting-started/dependabot-quickstart-guide&#34;&gt;dependabot&lt;/a&gt;&#xA;supports&#xA;&lt;a href=&#34;https://docs.github.com/en/code-security/dependabot/working-with-dependabot/dependabot-options-reference#groups--&#34;&gt;grouping&lt;/a&gt;&#xA;commits into fewer, targeted pull requests.&lt;/p&gt;&#xA;&lt;p&gt;A recent example in the repository of this blog:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-diff&#34;&gt;% git show&#xA;commit 1fa93bed4d0faa9dfcdd6e554201d708baa38dbb (HEAD -&amp;gt; master, origin/master, origin/HEAD)&#xA;Author: Thiago Perrotta &amp;lt;{email redacted}&amp;gt;&#xA;Date:   Wed May 14 15:08:23 2025 &amp;#43;0200&#xA;&#xA;    group dependabot updates&#xA;&#xA;diff --git .github/dependabot.yml .github/dependabot.yml&#xA;index d6ca206c..3a5d1b09 100644&#xA;--- .github/dependabot.yml&#xA;&amp;#43;&amp;#43;&amp;#43; .github/dependabot.yml&#xA;@@ -4,7 &amp;#43;4,15 @@ updates:&#xA;     directory: /&#xA;     schedule:&#xA;       interval: monthly&#xA;&amp;#43;    groups:&#xA;&amp;#43;      all:&#xA;&amp;#43;        patterns:&#xA;&amp;#43;          - &amp;#34;*&amp;#34;&#xA;   - package-ecosystem: github-actions&#xA;     directory: /&#xA;     schedule:&#xA;       interval: monthly&#xA;&amp;#43;    groups:&#xA;&amp;#43;      all:&#xA;&amp;#43;        patterns:&#xA;&amp;#43;          - &amp;#34;*&amp;#34;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;This is better for my workflow because then I will have less pull requests to&#xA;deal with at once, instead of having to review them one by one for each&#xA;individual github action or git submodule bump.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Github: dependabot: group updates&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>helm-secrets &#43; vals: string interpolation
      </title>
      <link>https://perrotta.dev/2025/05/helm-secrets--vals-string-interpolation/</link>
      <pubDate>Thu, 08 May 2025 15:25:14 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/05/helm-secrets--vals-string-interpolation/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://github.com/jkroepke/helm-secrets&#34;&gt;helm-secrets&lt;/a&gt; is:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;a helm plugin that helps to manage secrets with Git workflow and store them&#xA;anywhere.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;It uses &lt;a href=&#34;https://github.com/helmfile/vals&#34;&gt;vals&lt;/a&gt; underneath, which is a:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Helm-like configuration values loader with support for various sources&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;&amp;hellip;including, in particular, &lt;a href=&#34;https://www.hashicorp.com/en/products/vault&#34;&gt;HashiCorp Vault&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The &lt;code&gt;vals&lt;/code&gt; syntax is documented&#xA;&lt;a href=&#34;https://github.com/helmfile/vals?tab=readme-ov-file#expression-syntax&#34;&gt;in the vals documentation&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Problem statement&lt;/strong&gt;: construct a string composed of the following two vault&#xA;entries:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;vault://kv-v2/services/common/postgres/#url&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;vault://kv-v2/services/common/postgres/#params&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&amp;hellip;to yield &lt;code&gt;{url}?{params}&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;How do we concatenate two vault entries?&#xA;The secret is in the &lt;code&gt;+&lt;/code&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Finally, the optional trailing &lt;code&gt;+&lt;/code&gt; is the explicit &amp;ldquo;end&amp;rdquo; of the expression.&#xA;You usually don&amp;rsquo;t need it, as if omitted, it treats anything after &lt;code&gt;ref+&lt;/code&gt; and&#xA;before the new-line or the end-of-line as an expression to be evaluated. An&#xA;explicit &lt;code&gt;+&lt;/code&gt; is handy when you want to do a simple string interpolation. That&#xA;is, &lt;code&gt;foo ref+SECRET1+ ref+SECRET2+ bar&lt;/code&gt; evaluates to &lt;code&gt;foo SECRET1_VALUE SECRET2_VALUE bar&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The final result:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;secrets&amp;#43;literal://ref&amp;#43;vault://kv-v2/services/common/postgres/#url&amp;#43;?ref&amp;#43;vault://kv-v2/services/common/postgres/#params&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&amp;hellip;wherein &lt;code&gt;secrets+literal://&lt;/code&gt; comes from &lt;code&gt;helm-secrets&lt;/code&gt;. It needs to appear only&#xA;once. &lt;code&gt;ref+vault://&lt;/code&gt; comes from &lt;code&gt;vals&lt;/code&gt;, it appears once for each vault entry.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: helm-secrets + vals: string interpolation&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>1Password SSH agent error
      </title>
      <link>https://perrotta.dev/2025/05/1password-ssh-agent-error/</link>
      <pubDate>Wed, 07 May 2025 12:52:00 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>macos</category>
      <category>security</category>
      <category>ssh</category>
      <guid>https://perrotta.dev/2025/05/1password-ssh-agent-error/</guid>
      <description>&lt;p&gt;♠ A day in the life:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% ssh {my-server}&#xA;STDERR:&#xA;    sign_and_send_pubkey: signing failed for ED25519 &amp;#34;/Users/thiago/.ssh/id_ed25519&amp;#34; from agent: agent refused operation&#xA;&#xA;    git@github.com: Permission denied (publickey).&#xA;&#xA;    fatal: Could not read from remote repository.&#xA;&#xA;    Please make sure you have the correct access rights&#xA;&#xA;    and the repository exists.&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;What&amp;rsquo;s the matter?&lt;/p&gt;&#xA;&lt;p&gt;My ssh config is integrated with the &lt;a href=&#34;https://developer.1password.com/docs/ssh/agent/&#34;&gt;1Password ssh&#xA;agent&lt;/a&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;Host *&#xA;        # Use 1Password SSH agent when available&#xA;        # macOS only&#xA;        # Linux: &amp;#34;~/.1password/agent.sock&amp;#34;&#xA;        IdentityAgent &amp;#34;~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock&amp;#34;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Ensure 1Password is unlocked, so that the agent can be used.&lt;/p&gt;&#xA;&lt;p&gt;It can be unlocked from its macOS app, or from the CLI (&lt;code&gt;op signin --account {my-account}&lt;/code&gt;).&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: 1Password SSH agent error&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/macos/&#34;&gt;#macos&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/ssh/&#34;&gt;#ssh&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>HashiCorp Vault from helm values
      </title>
      <link>https://perrotta.dev/2025/04/hashicorp-vault-from-helm-values/</link>
      <pubDate>Wed, 23 Apr 2025 16:56:16 +0200</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>kubernetes</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/04/hashicorp-vault-from-helm-values/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: Populate HashiCorp Vault with select keys from &lt;code&gt;helm get values&lt;/code&gt; for a given chart release.&lt;/p&gt;&#xA;&lt;p&gt;The following script works:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/bash&#xA;# Usage: Tweak entries as needed and then run the script.&#xA;&#xA;set -euo pipefail&#xA;&#xA;# It&amp;#39;s an associative array: a dictionary, a hash map&#xA;#&#xA;#   Key: the entry to be populated in vault&#xA;#   Value: a `jq` JSON selector from the output of `helm get values -o json`&#xA;declare -A entries=(&#xA;    # keep-sorted start&#xA;    [config.aws.accessKey]=&amp;#34;.config.aws.accessKey&amp;#34;&#xA;    [config.aws.secretKey]=&amp;#34;.config.aws.secretKey&amp;#34;&#xA;    [config.cronjob.apiPassword]=&amp;#34;.config.cronjob.apiPassword&amp;#34;&#xA;    [config.cronjob.apiUser]=&amp;#34;.config.cronjob.apiUser&amp;#34;&#xA;    [config.postgresql.adminpass]=&amp;#34;.config.postgresql.adminpass&amp;#34;&#xA;    [config.postgresql.password]=&amp;#34;.config.postgresql.password&amp;#34;&#xA;    [config.postgresql.ropass]=&amp;#34;.config.postgresql.ropass&amp;#34;&#xA;    [config.postgresql.rwpass]=&amp;#34;.config.postgresql.rwpass&amp;#34;&#xA;    [config.slack.token]=&amp;#34;.config.slack.token&amp;#34;&#xA;    [config.slack.verificationToken]=&amp;#34;.config.slack.verificationToken&amp;#34;&#xA;    # keep-sorted end&#xA;)&#xA;&#xA;main() {&#xA;        vault login&#xA;&#xA;        FIRST=1&#xA;        for entry in &amp;#34;${!entries[@]}&amp;#34;; do&#xA;                key=&amp;#34;$entry&amp;#34;&#xA;                value=&amp;#34;${entries[$entry]}&amp;#34;&#xA;                content=&amp;#34;$(helm get values -n tools mychart -o json | jq -r &amp;#34;$value&amp;#34;)&amp;#34;&#xA;&#xA;                echo &amp;#34;Populating vault $key from helm value $key: $content...&amp;#34;&#xA;&#xA;                if [[ &amp;#34;$FIRST&amp;#34; == &amp;#34;1&amp;#34; ]]; then&#xA;                  vault kv put -mount=&amp;#34;kv-v2&amp;#34; &amp;#34;services/mychart/&amp;#34; &amp;#34;$key=$content&amp;#34;&#xA;                  FIRST=0&#xA;                else&#xA;                  vault kv patch -mount=&amp;#34;kv-v2&amp;#34; &amp;#34;services/mychart/&amp;#34; &amp;#34;$key=$content&amp;#34;&#xA;                fi&#xA;        done&#xA;&#xA;        vault kv get -mount=&amp;#34;kv-v2&amp;#34; &amp;#34;services/mychart&amp;#34;&#xA;&#xA;        rm ~/.vault-token&#xA;}&#xA;&#xA;main&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;code&gt;FIRST&lt;/code&gt;: In the first time we need to create an entry (&lt;code&gt;vault kv put&lt;/code&gt;), in the&#xA;second time we need to patch it (&lt;code&gt;vault kv patch&lt;/code&gt;).&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: HashiCorp Vault from helm values&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/kubernetes/&#34;&gt;#kubernetes&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>★ 1Password CLI &#43; direnv integration
      </title>
      <link>https://perrotta.dev/2025/03/1password-cli--direnv-integration/</link>
      <pubDate>Wed, 26 Mar 2025 14:15:15 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>bestof</category>
      <category>dev</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/03/1password-cli--direnv-integration/</guid>
      <description>&lt;p&gt;♠ Let&amp;rsquo;s stay you&amp;rsquo;re working on a terraform module that has a sensitive variable&#xA;named &lt;code&gt;api_key&lt;/code&gt; in &lt;code&gt;variables.tf&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;When running &lt;code&gt;terraform apply&lt;/code&gt;, you&amp;rsquo;ll need to supply its value. Every. Single.&#xA;Time.&lt;/p&gt;&#xA;&lt;p&gt;We can make it persistent by setting &lt;code&gt;export TF_VAR_api_key={my value}&lt;/code&gt;.&#xA;However this is only persistent within the current shell.&lt;/p&gt;&#xA;&lt;p&gt;To make it persistent across multiple sessions, create a &lt;code&gt;.envrc&lt;/code&gt; file at the&#xA;root module:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/sh&#xA;export TF_VAR_api_key={my value}&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Then source it: &lt;code&gt;source ~/.envrc&lt;/code&gt;. Now you have to source it every single time&#xA;you open a new shell.&lt;/p&gt;&#xA;&lt;p&gt;We can use &lt;a href=&#34;http://direnv.net/&#34;&gt;&lt;code&gt;direnv&lt;/code&gt;&lt;/a&gt; to do so automatically. It has been&#xA;&lt;a href=&#34;https://perrotta.dev/2022/01/direnv-automate-your-environment-variables/&#34;&gt;previously covered&lt;/a&gt;. But&amp;hellip;now we have&#xA;a secret stored as plain text in our filesystem.&lt;/p&gt;&#xA;&lt;p&gt;We could store it in a more secure place, like a password manager, and then have&#xA;a mechanism automatically fetch its value.&lt;/p&gt;&#xA;&lt;h2 id=&#34;first-approach&#34;&gt;&#xA;  First approach&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2025/03/1password-cli--direnv-integration/#first-approach&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;When using 1Password, the &lt;a href=&#34;https://developer.1password.com/docs/cli/&#34;&gt;1Password&#xA;CLI&lt;/a&gt; is such a mechanism.&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/sh&#xA;export TF_VAR_api_key=$(op run --no-masking -- op read &amp;#34;op://{vault name}/{entry name}/{item name}&amp;#34;)&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;You can fetch the secret reference path from 1Password:&#xA;&lt;a href=&#34;https://developer.1password.com/docs/cli/secret-references/&#34;&gt;docs&lt;/a&gt;. Click &amp;ldquo;Copy&#xA;Secret Reference&amp;rdquo; in the corresponding entry item.&lt;/p&gt;&#xA;&lt;h2 id=&#34;second-approach&#34;&gt;&#xA;  Second approach&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2025/03/1password-cli--direnv-integration/#second-approach&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;The above is one possibility, and it works flawlessly, but there&amp;rsquo;s a second&#xA;approach that uses &lt;code&gt;op run&lt;/code&gt; alone with a secret reference that is dynamically&#xA;replaced.&lt;/p&gt;&#xA;&lt;p&gt;Create an &lt;code&gt;.env&lt;/code&gt; file:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;TF_VAR_api_key=&amp;#34;op://{vault name}/{entry name}/{item name}&amp;#34;&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Update the &lt;code&gt;.envrc&lt;/code&gt; file:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;#!/bin/sh&#xA;dotenv&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Now run &lt;code&gt;terraform apply&lt;/code&gt; via &lt;code&gt;op run&lt;/code&gt;, as follows:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;op run --env-file=.env -- terraform apply&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;h2 id=&#34;references&#34;&gt;&#xA;  References&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2025/03/1password-cli--direnv-integration/#references&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://mattedwards.org/2024/01/inject-secrets-into-terraform-environment-using-1password/&#34;&gt;https://mattedwards.org/2024/01/inject-secrets-into-terraform-environment-using-1password/&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://nshipster.com/1password-cli/&#34;&gt;https://nshipster.com/1password-cli/&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: 1Password CLI + direnv integration&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/bestof/&#34;&gt;#bestof&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>★ AWS IAM resources
      </title>
      <link>https://perrotta.dev/2025/03/aws-iam-resources/</link>
      <pubDate>Thu, 13 Mar 2025 15:30:10 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>bestof</category>
      <category>dev</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/03/aws-iam-resources/</guid>
      <description>&lt;p&gt;♠ When working on Amazon Web Services (AWS), at some point you&amp;rsquo;ll need to fiddle&#xA;with &lt;a href=&#34;https://en.wikipedia.org/wiki/Identity_and_access_management&#34;&gt;IAM&lt;/a&gt;&#xA;(Identity and access management).&lt;/p&gt;&#xA;&lt;p&gt;It can be daunting to navigate the massive &lt;a href=&#34;https://docs.aws.amazon.com/iam/&#34;&gt;AWS&#xA;Documentation&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;I am collecting a couple of useful resources in this post.&lt;/p&gt;&#xA;&lt;h2 id=&#34;permissions&#34;&gt;&#xA;  Permissions&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2025/03/aws-iam-resources/#permissions&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://aws.permissions.cloud/usage&#34;&gt;https://aws.permissions.cloud/usage&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;The aws.permissions.cloud website uses a variety of information gathered&#xA;within the &lt;a href=&#34;https://github.com/iann0036/iam-dataset&#34;&gt;IAM Dataset&lt;/a&gt; and exposes&#xA;that information in a clean, easy-to-read format.&lt;/p&gt;&#xA;&lt;p&gt;aws.permissions.cloud was built in order to provide an alternate,&#xA;community-driven source of truth for AWS identity.&lt;/p&gt;&#xA;&lt;p&gt;The website can be navigated using the left sidebar or by quickly looking up a&#xA;specific managed policy, IAM permission or API method in the top search bar.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;For example, search for &amp;ldquo;route53&amp;rdquo; or for &amp;ldquo;s3&amp;rdquo;.&lt;/p&gt;&#xA;&lt;p&gt;The main goal of figuring out &lt;strong&gt;permissions&lt;/strong&gt; is to add them to a &lt;strong&gt;policy&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;policies&#34;&gt;&#xA;  Policies&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2025/03/aws-iam-resources/#policies&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://aws.permissions.cloud/policyevaluator&#34;&gt;https://aws.permissions.cloud/policyevaluator&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;p&gt;Use the Policy Evaluator to validate your &lt;a href=&#34;https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_resource.html&#34;&gt;policy&#xA;JSON&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;A policy associates permissions (actions) with resources (ARNs).&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Enter your IAM policy in the box below.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;{&#xA;    &amp;#34;Version&amp;#34;: &amp;#34;2012-10-17&amp;#34;,&#xA;    &amp;#34;Statement&amp;#34;: [&#xA;        {&#xA;            &amp;#34;Action&amp;#34;: [&#xA;              &amp;#34;route53:ChangeResourceRecordSets&amp;#34;&#xA;            ],&#xA;            &amp;#34;Effect&amp;#34;: &amp;#34;Allow&amp;#34;,&#xA;            &amp;#34;Resource&amp;#34;: [&#xA;              &amp;#34;arn:aws:route53:::hostedzone/01234567890123&amp;#34;,&#xA;              &amp;#34;arn:aws:route53:::hostedzone/01234567890124&amp;#34;&#xA;            ]&#xA;        }&#xA;    ]&#xA;}&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Each resource above represents a distinct &lt;a href=&#34;https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/hosted-zones-working-with.html&#34;&gt;hosted zone&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;A hosted zone is a container for records, and records contain information&#xA;about how you want to route traffic for a specific domain, such as&#xA;example.com, and its subdomains (acme.example.com, zenith.example.com). A&#xA;hosted zone and the corresponding domain have the same name. There are two&#xA;types of hosted zones: public and private.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;code&gt;&amp;quot;Resource&amp;quot;&lt;/code&gt; is singular but it accepts an array of ARNs as well.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: Wildcards (&lt;code&gt;&#39;*&#39;&lt;/code&gt;) are accepted e.g. &lt;code&gt;&amp;quot;arn:aws:route53:::hostedzone/*&amp;quot;&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Another example, with S3 buckets:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-json&#34;&gt;{&#xA;    &amp;#34;Version&amp;#34;: &amp;#34;2012-10-17&amp;#34;,&#xA;    &amp;#34;Statement&amp;#34;: [&#xA;        {&#xA;            &amp;#34;Sid&amp;#34;: &amp;#34;VisualEditor0&amp;#34;,&#xA;            &amp;#34;Effect&amp;#34;: &amp;#34;Allow&amp;#34;,&#xA;            &amp;#34;Action&amp;#34;: &amp;#34;s3:ListBucket&amp;#34;,&#xA;            &amp;#34;Resource&amp;#34;: &amp;#34;arn:aws:s3:::*&amp;#34;&#xA;        },&#xA;        {&#xA;            &amp;#34;Sid&amp;#34;: &amp;#34;VisualEditor1&amp;#34;,&#xA;            &amp;#34;Effect&amp;#34;: &amp;#34;Allow&amp;#34;,&#xA;            &amp;#34;Action&amp;#34;: [&#xA;                &amp;#34;s3:PutObject&amp;#34;,&#xA;                &amp;#34;s3:GetObject&amp;#34;&#xA;            ],&#xA;            &amp;#34;Resource&amp;#34;: &amp;#34;arn:aws:s3:::*/*&amp;#34;&#xA;        }&#xA;    ]&#xA;}&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;&lt;code&gt;Action&lt;/code&gt; can be either a single entry or an array.&lt;/p&gt;&#xA;&lt;h3 id=&#34;testing&#34;&gt;&#xA;  Testing&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2025/03/aws-iam-resources/#testing&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Test out policies and permissions with the &lt;a href=&#34;https://policysim.aws.amazon.com/&#34;&gt;IAM Policy&#xA;Simulator&lt;/a&gt;. This service is provided by AWS&#xA;itself.&#xA;&lt;a href=&#34;https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_testing-policies.html&#34;&gt;Docs&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;terraform&#34;&gt;&#xA;  Terraform&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2025/03/aws-iam-resources/#terraform&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Terraform Registry: &lt;a href=&#34;https://registry.terraform.io/providers/hashicorp/aws/latest/docs&#34;&gt;AWS Provider Docs&lt;/a&gt;.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Update(2025-09-17)&lt;/strong&gt;:&lt;/p&gt;&#xA;&lt;h3 id=&#34;roles&#34;&gt;&#xA;  Roles&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2025/03/aws-iam-resources/#roles&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h3&gt;&#xA;&lt;p&gt;Permissions and permissions policies can be associated with roles.&lt;/p&gt;&#xA;&lt;p&gt;A role is an entity kind that can be &lt;em&gt;assumed&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Roles can have trust relationships, which are entities that can assume the role under specified conditions.&lt;/p&gt;&#xA;&lt;p&gt;For example, it&amp;rsquo;s possible to associate GitHub as a federated web identity provider (IdP) for a given role.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: AWS IAM resources&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/bestof/&#34;&gt;#bestof&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>SAML Chrome Panel
      </title>
      <link>https://perrotta.dev/2025/03/saml-chrome-panel/</link>
      <pubDate>Wed, 12 Mar 2025 00:51:05 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/03/saml-chrome-panel/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://chromewebstore.google.com/detail/saml-chrome-panel/paijfdbeoenhembfhkhllainmocckace&#34;&gt;SAML Chrome Panel&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Extends the (Chrome) Developer Tools, adding support for SAML Requests and&#xA;Responses to be displayed in the Developer Tools window&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;This is an Open Source SAML debugger for Chrome. It operates as another panel&#xA;in the Chrome Developer Tools section, which monitors the traffic in the&#xA;current active tab. This panel is trying to replicate what the Firefox version&#xA;of SAML Tracer does as there wasn&amp;rsquo;t a good enough one (or any) for Chrome at&#xA;the time of writing this.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;It is designed to display all network traffic, along with the request and&#xA;response data. If there is a SAML request or response, then it will grab the&#xA;message, format it nicely and show it to you in another tab.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Upstream: &lt;a href=&#34;https://github.com/milton-lai/saml-chrome-panel&#34;&gt;https://github.com/milton-lai/saml-chrome-panel&lt;/a&gt;&lt;/p&gt;&#xA;&lt;p&gt;This Chrome extension has proven useful to me at least twice.&lt;/p&gt;&#xA;&lt;p&gt;Recently I was integrating &lt;a href=&#34;https://argo-cd.readthedocs.io/en/stable/&#34;&gt;ArgoCD&lt;/a&gt;&#xA;and &lt;a href=&#34;https://www.mongodb.com/atlas&#34;&gt;MongoDB Atlas&lt;/a&gt; with&#xA;&lt;a href=&#34;https://www.okta.com/&#34;&gt;Okta&lt;/a&gt;. You can do it via SAML or via OIDC.&lt;/p&gt;&#xA;&lt;p&gt;There&amp;rsquo;s always a chance of having one tiny configuration mistake, perhaps a&#xA;typo or a missing field. I found:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ArgoCD: a typo in the callback URL (&lt;code&gt;foo-foo&lt;/code&gt; instead of &lt;code&gt;foo&lt;/code&gt;)&lt;/li&gt;&#xA;&lt;li&gt;MongoDB Atlas: an &lt;code&gt;acsError&lt;/code&gt; in the response&#xA;(&lt;a href=&#34;https://www.mongodb.com/community/forums/t/mongodb-atlas-okta-integration-login-error/230897&#34;&gt;example&lt;/a&gt;,&#xA;&lt;a href=&#34;https://www.mongodb.com/community/forums/t/sso-issue-with-idp-azure-entraid-okta-mongodb-saml/278500&#34;&gt;example&lt;/a&gt;):&#xA;&lt;a href=&#34;https://cloud.mongodb.com/okta/hooks/acsError&#34;&gt;https://cloud.mongodb.com/okta/hooks/acsError&lt;/a&gt;: it was a certificate error.&#xA;Instead of copying and pasting the certificate content (&lt;code&gt;------ BEGIN CERTIFICATE ------&lt;/code&gt;), upload the file directly. The&#xA;&lt;a href=&#34;https://www.mongodb.com/docs/cloud-manager/security/federated-auth-okta/&#34;&gt;documentation&lt;/a&gt;&#xA;(item &lt;code&gt;1.h.&lt;/code&gt;) notes so. Convert it to &lt;code&gt;.pem&lt;/code&gt; prior to uploading it.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: SAML Chrome Panel&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>★ Have I Been Pwned?
      </title>
      <link>https://perrotta.dev/2025/03/have-i-been-pwned/</link>
      <pubDate>Tue, 11 Mar 2025 16:29:34 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>bestof</category>
      <category>dev</category>
      <category>privacy</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/03/have-i-been-pwned/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://haveibeenpwned.com/&#34;&gt;Have I Been Pwned?&lt;/a&gt; by the great &lt;a href=&#34;https://www.troyhunt.com/&#34;&gt;Troy Hunt&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Check if your email address is in a data breach&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;h2 id=&#34;individual-sign-up&#34;&gt;&#xA;  Individual sign-up&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2025/03/have-i-been-pwned/#individual-sign-up&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Sign up and then verify your email:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;You&amp;rsquo;ve just been sent a verification email, all you need to do now is confirm&#xA;your address by clicking on the link when it hits your mailbox and you&amp;rsquo;ll be&#xA;automatically notified of future pwnage. In case it doesn&amp;rsquo;t show up, check&#xA;your junk mail and if you still can&amp;rsquo;t find it, you can always repeat this&#xA;process.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;The email:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;You (or possibly someone else), just subscribed &lt;code&gt;{email}&lt;/code&gt; to the&#xA;notification service that will automatically let you know if your address is&#xA;caught up in a future data breach. It&amp;rsquo;s a free service and you can unsubscribe&#xA;at any time if you don&amp;rsquo;t want the notifications.&lt;/p&gt;&#xA;&lt;p&gt;Just to confirm that you&amp;rsquo;re a real person behind a real email address, click&#xA;on the link below then you&amp;rsquo;ll be automatically subscribed to new breach&#xA;notifications where this email address has been pwned.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Then:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Verification complete&lt;/p&gt;&#xA;&lt;p&gt;All done, you&amp;rsquo;ll be sent an email if this address gets pwned in future, below&#xA;is your existing exposure&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;There&amp;rsquo;s a lot of lore in the &lt;a href=&#34;https://www.goodreads.com/book/show/74960566-pwned---the-collected-blog-posts&#34;&gt;PWNED — The Collected Blog&#xA;Posts&lt;/a&gt;&#xA;book by Troy Hunt &amp;amp; Rob Conery, which I would recommend.&lt;/p&gt;&#xA;&lt;h2 id=&#34;domain-sign-up&#34;&gt;&#xA;  Domain sign-up&#xA;  &lt;a class=&#34;heading-anchor&#34; href=&#34;https://perrotta.dev/2025/03/have-i-been-pwned/#domain-sign-up&#34; aria-label=&#34;Link to this section&#34;&gt;#&lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;Furthermore, there&amp;rsquo;s also a domain search service:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Search for pwned accounts across an entire domain and receive future&#xA;notifications.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Domain search allows you to find all breached email addresses on a domain you&#xA;control via a dedicated domain search dashboard. Once verified, you will also&#xA;receive notifications via email if they appear in future breaches. Before you&#xA;can perform a domain search, you need to verify your email address and that&#xA;you control the domains you&amp;rsquo;re searching. If you cannot verify that you&#xA;control a domain, you will not be able to search for breached email addresses&#xA;on it.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Upon signing up:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Welcome to the Have I Been Pwned domain search&lt;/p&gt;&#xA;&lt;p&gt;You (or possibly someone else), just began the process to access the domain&#xA;search dashboard using the email address &lt;code&gt;{email}&lt;/code&gt;. Just to confirm&#xA;that you&amp;rsquo;re a real person behind a real email address, click on the link below&#xA;which will take you to the dashboard (the link will remain valid for 60&#xA;minutes).&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Then you can choose a way to verify your domain:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;with a pre-defined address like &lt;code&gt;security@&lt;/code&gt; or &lt;code&gt;webmaster@&lt;/code&gt; or &lt;code&gt;postmaster@&lt;/code&gt;&#xA;or &lt;code&gt;hostmaster@&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;with a DNS TXT record&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;When following the first route:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;An email containing a verification token has been sent off to the address you&#xA;chose, just copy and paste it into the text box below and you&amp;rsquo;ll be able to&#xA;perform the domain search right after that.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;I was already familiar with the individual service, but only now discovered the&#xA;domain service, at work, by happenstance.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Have I Been Pwned?&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/bestof/&#34;&gt;#bestof&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/privacy/&#34;&gt;#privacy&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>★ DuckDuckGo email protection
      </title>
      <link>https://perrotta.dev/2025/02/duckduckgo-email-protection/</link>
      <pubDate>Sat, 08 Feb 2025 14:50:39 +0100</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>bestof</category>
      <category>privacy</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/02/duckduckgo-email-protection/</guid>
      <description>&lt;p&gt;♠ One of my best discoveries this year: &lt;a href=&#34;https://duckduckgo.com/duckduckgo-help-pages/email-protection/&#34;&gt;DuckDuckGo email&#xA;protection&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;DuckDuckGo Email Protection is a free email forwarding service that removes&#xA;multiple types of hidden email trackers and lets you create multiple unique&#xA;private email addresses on the fly. Email Protection works with your current&#xA;email account – no need to change providers or juggle multiple inboxes.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/2022/01/throwaway-disposable-accounts-for-ephemeral-use-cases/&#34;&gt;Previously&lt;/a&gt;: &lt;a href=&#34;https://10minutemail.com&#34;&gt;https://10minutemail.com&lt;/a&gt;, &lt;a href=&#34;https://dispostable.com&#34;&gt;https://dispostable.com&lt;/a&gt;, etc.&lt;/p&gt;&#xA;&lt;p&gt;DuckDuckGo is (IMHO) better. Why?&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;First of all, you can create a memorable and fixed email, which acts as an&#xA;entrypoint. For example, &lt;code&gt;foo@duck.com&lt;/code&gt;. In principle you could keep using&#xA;this email for all new services, especially when in a hurry. Emails delivered&#xA;there get redirected to a forwarding email of your choice. Thus this&#xA;integrates with your existing setup, and there&amp;rsquo;s no need to bypass CAPTCHAs&#xA;just for the sake of reading emails, and you get to optionally keep them&#xA;archived should you want to.&lt;/li&gt;&#xA;&lt;li&gt;The added value comes from throwaway, disposable emails though: DuckDuckGo&#xA;allows you to generate one-off emails in the form&#xA;&lt;code&gt;word1-word2-word3@duck.com&lt;/code&gt;. You can create as many of these as you want,&#xA;with a single click. These also get redirected to the previously specified&#xA;forwarding email address.&lt;/li&gt;&#xA;&lt;li&gt;The emails are scrubbed so to remove tracking and analytics. You get to know&#xA;whether there were any trackers in the original email and, if yes, which ones&#xA;were removed.&lt;/li&gt;&#xA;&lt;li&gt;You can disassociate these emails at any time. It&amp;rsquo;s very easy to do so, by&#xA;clicking a link that is embedded into each received email.&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;@duck.com&lt;/code&gt; tends to be more reputable than other email services. Which means,&#xA;chances are, it is less likely to be blocked. I ran into roadblocks many times&#xA;when trying to use &lt;code&gt;@dispostable.com&lt;/code&gt; to sign up with throwaway accounts.&lt;/li&gt;&#xA;&lt;li&gt;It&amp;rsquo;s completely free! A comparable service is &lt;a href=&#34;https://support.apple.com/en-ca/guide/icloud/mm9d9012c9e8/icloud&#34;&gt;iCloud+ Hide My&#xA;Email&lt;/a&gt; by&#xA;Apple, but it&amp;rsquo;s not free. Also, the Duck emails are manageable from the web,&#xA;whereas iCloud+ can only be managed from Apple devices (as far as I know).&lt;/li&gt;&#xA;&lt;li&gt;It integrates seamlessly with sign-up flows if you use its &lt;a href=&#34;https://duckduckgo.com/duckduckgo-help-pages/desktop/chrome/&#34;&gt;Chrome&#xA;extension&lt;/a&gt;.&#xA;Similar to how password managers typically integrate with sign-up forms.&lt;/li&gt;&#xA;&lt;li&gt;The DuckDuckGo mobile app also supports it.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Ever since adopting this workflow I find it&amp;rsquo;s much easier to sign up to try out&#xA;new services and/or websites, without having to use my personal email and&#xA;worrying whether it would be possible to delete my account later on.&lt;/p&gt;&#xA;&lt;p&gt;Furthermore it&amp;rsquo;s very easy to organize these emails in my client, as all I have&#xA;to do is match emails coming from &lt;code&gt;@duck.com&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;And I don&amp;rsquo;t have to worry about &amp;ldquo;wasting&amp;rdquo; my personal domain email address space&#xA;with catch-all sign-ups (e.g. &lt;code&gt;foo at perrotta . dev&lt;/code&gt;).&lt;/p&gt;&#xA;&lt;p&gt;The only downside is that it&amp;rsquo;s not possible to &lt;em&gt;send&lt;/em&gt; emails from these&#xA;addresses. Therefore, it&amp;rsquo;s not an universal solution.&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;Update(2025-05-10)&lt;/strong&gt;: Alternative: &lt;a href=&#34;https://simplelogin.io/&#34;&gt;Simple Login&lt;/a&gt; by Proton.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: DuckDuckGo email protection&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/bestof/&#34;&gt;#bestof&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/privacy/&#34;&gt;#privacy&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Cloudflare DNS for families
      </title>
      <link>https://perrotta.dev/2025/01/cloudflare-dns-for-families/</link>
      <pubDate>Tue, 21 Jan 2025 01:42:59 -0300</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>privacy</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/01/cloudflare-dns-for-families/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://developers.cloudflare.com/1.1.1.1/ip-addresses/&#34;&gt;Cloudflare&lt;/a&gt; has one of&#xA;the coolest DNS IP addresses on the planet: &lt;code&gt;1.1.1.1&lt;/code&gt; (primary) and &lt;code&gt;1.0.0.1&lt;/code&gt;&#xA;(secondary).&lt;/p&gt;&#xA;&lt;p&gt;Furthermore they have another class of DNS servers &lt;a href=&#34;https://blog.cloudflare.com/introducing-1-1-1-1-for-families/&#34;&gt;&amp;ldquo;for&#xA;families&amp;rdquo;&lt;/a&gt;,&#xA;intended to block malware and/or adult content.&lt;/p&gt;&#xA;&lt;p&gt;They are also quite memorable:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Malware: &lt;code&gt;1.1.1.2&lt;/code&gt;, &lt;code&gt;1.0.0.2&lt;/code&gt;&lt;/li&gt;&#xA;&lt;li&gt;Adult content: &lt;code&gt;1.1.1.3&lt;/code&gt;, &lt;code&gt;1.0.0.3&lt;/code&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Cloudflare DNS for families&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/privacy/&#34;&gt;#privacy&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Quick and dirty random password generator
      </title>
      <link>https://perrotta.dev/2025/01/quick-and-dirty-random-password-generator/</link>
      <pubDate>Thu, 09 Jan 2025 16:41:15 -0300</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>privacy</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/01/quick-and-dirty-random-password-generator/</guid>
      <description>&lt;p&gt;♠ On Linux / macOS, use &lt;code&gt;/dev/urandom&lt;/code&gt;, with a &lt;code&gt;tr&lt;/code&gt; pass-through filter:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% tr -dc &amp;#39;A-Za-z0-9&amp;#39; &amp;lt; /dev/urandom | head -c 20 &amp;amp;&amp;amp; echo&#xA;BLH1gVgdukdmTcvopOuC&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;In the example above only alphanumeric characters are allowed.&lt;/p&gt;&#xA;&lt;p&gt;There are other popular ways to do so:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Dice (&lt;a href=&#34;https://diceware.dmuth.org/&#34;&gt;Douglas Muth&lt;/a&gt;,&#xA;&lt;a href=&#34;https://www.eff.org/dice&#34;&gt;EFF&lt;/a&gt;).&lt;/li&gt;&#xA;&lt;li&gt;&lt;a href=&#34;https://pwgen.sourceforge.net/&#34;&gt;&lt;code&gt;pwgen&lt;/code&gt;&lt;/a&gt; (via &lt;code&gt;brew home pwgen&lt;/code&gt;)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Quick and dirty random password generator&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/privacy/&#34;&gt;#privacy&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>nginx: add basic auth
      </title>
      <link>https://perrotta.dev/2025/01/nginx-add-basic-auth/</link>
      <pubDate>Tue, 07 Jan 2025 03:28:02 -0300</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/01/nginx-add-basic-auth/</guid>
      <description>&lt;p&gt;♠ &lt;strong&gt;Problem statement&lt;/strong&gt;: You want to expose a HTTPS service from your cloud VPS to&#xA;the public Internet. You do not wish to use a VPN (e.g.&#xA;&lt;a href=&#34;https://tailscale.com/&#34;&gt;tailscale&lt;/a&gt;) to do so – which is a great way to address&#xA;this, but it&amp;rsquo;s out of scope in this particular instance. How to proceed?&lt;/p&gt;&#xA;&lt;p&gt;Here&amp;rsquo;s a simple yet effective way: use / configure &lt;a href=&#34;https://nginx.org/&#34;&gt;nginx&lt;/a&gt;&#xA;as a reverse proxy.&lt;/p&gt;&#xA;&lt;p&gt;Then add &lt;a href=&#34;https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication&#34;&gt;HTTP basic&#xA;auth&lt;/a&gt; to it:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% doas htpasswd -c /etc/nginx/.htpasswd myuser&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;The command will prompt for a password.&lt;/p&gt;&#xA;&lt;p&gt;Once it&amp;rsquo;s set, add two lines to the corresponding &lt;code&gt;server {}&lt;/code&gt; block in your&#xA;nginx config:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-nginxconf&#34;&gt;server {&#xA;[...]&#xA;    auth_basic &amp;#34;Restricted Access&amp;#34;;&#xA;    auth_basic_user_file /etc/nginx/.htpasswd;&#xA;[...]&#xA;}&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;And finally: run &lt;code&gt;nginx -s reload&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Now, whenever you navigate to that host, you&amp;rsquo;ll be prompted to enter the basic&#xA;HTTP auth creds above.&lt;/p&gt;&#xA;&lt;p&gt;The beauty of this approach is that it works for any HTTPS server, as it is&#xA;service agnostic.&lt;/p&gt;&#xA;&lt;p&gt;The next level would be to integrate an SSO solution such as&#xA;&lt;a href=&#34;https://goauthentik.io/&#34;&gt;Authentik&lt;/a&gt;, however it&amp;rsquo;s quite more complex.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: nginx: add basic auth&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>iOS lockdown mode
      </title>
      <link>https://perrotta.dev/2025/01/ios-lockdown-mode/</link>
      <pubDate>Sat, 04 Jan 2025 23:58:11 -0300</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>privacy</category>
      <category>security</category>
      <guid>https://perrotta.dev/2025/01/ios-lockdown-mode/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://www.dedoimedo.com/computers/iphone-lockdown-mode.html&#34;&gt;dedoimedo&lt;/a&gt; makes&#xA;me realize that the iOS lockdown mode is actually quite usable, even for us&#xA;normies:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;For a while now, I&amp;rsquo;ve been hearing about this tool. Mostly in regard to&#xA;high-profile zero-day, zero-click, hands-free, security vulnerabilities in&#xA;iPhones and subsequent hacking of said devices by nation-state actors. Sounds&#xA;blockbustery, which piqued my interest. I am usually rather disdainful of&#xA;mainstream media reporting of tech affairs, but that does not mean there isn&amp;rsquo;t&#xA;something genuinely cool behind flashy, scary headlines. And so I decided to&#xA;test the Lockdown Mode.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;What are its effects?&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Specifically, most attachments are blocked in Messages, websites that use&#xA;JavaScript engines that run Just-in-Time (JIT) compilers or use remote fonts&#xA;will not render their pages correctly, various app invitations and calls are&#xA;blocked from people you haven&amp;rsquo;t approved beforehand, location information for&#xA;shared photos is excluded, Wi-Fi connectivity to insecure networks is limited,&#xA;and so on.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Aren&amp;rsquo;t these all sensible defaults anyway?&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Pretty much everything in the list above makes absolute sense. None of the&#xA;&amp;ldquo;restricted&amp;rdquo; features are actually important or desired. Think about it. Calls&#xA;from people you don&amp;rsquo;t know? Check! Photo location blocking? Check! Web pages&#xA;with pointless remote fonts? Double check! Sounds like an Internet reset,&#xA;going back to 2010, when the computing world was nicer, saner, cleaner!&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Enabling it is trivial, the prompts are perhaps a bit scarier than necessary&#xA;though:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;You get a whole bunch of explanations and warnings. Confirm. Reboot. The&#xA;iPhone will load without any visible signs that it&amp;rsquo;s actually running in a&#xA;&amp;ldquo;restricted&amp;rdquo; mode. Now. Launch Safari, and you will see a notification&#xA;displayed below the address bar. This is the first indication that the phone&#xA;is using the Lockdown Mode. But that does not mean you will be affected. Far&#xA;from it. I spent quite some time browsing, and I didn&amp;rsquo;t notice any website not&#xA;working correctly. Then again, the sites I go to are normal, sane sites, with&#xA;no sensory overload.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Initially Logseq and Obsidian would not properly work: they didn&amp;rsquo;t seem to be&#xA;able to read files from iCloud. I had to explicitly whitelist them in lockdown&#xA;mode settings. All other apps worked just fine.&lt;/p&gt;&#xA;&lt;p&gt;When web browsing with Safari I didn&amp;rsquo;t find any issue (so far) in the websites I&#xA;normally surf. The only noticeable UI difference is that, as web fonts are&#xA;blocked, the visual identity of websites are gone&lt;sup id=&#34;fnref:1&#34;&gt;&lt;a href=&#34;https://perrotta.dev/2025/01/ios-lockdown-mode/#fn:1&#34; class=&#34;footnote-ref&#34; role=&#34;doc-noteref&#34;&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;&#xA;&lt;p&gt;This is not necessarily a negative point: blocking web fonts has the positive&#xA;side effect of loading less remote data / reducing bandwidth.&lt;/p&gt;&#xA;&lt;p&gt;Furthermore, by making websites look more generic, perhaps that doubles-down as&#xA;a feature to make them less appealing / interesting, akin to using a&#xA;software filter to make your phone display everything in black-and-white; we&amp;rsquo;re&#xA;all spending a lot of screen time after all.&lt;/p&gt;&#xA;&lt;p&gt;I am curious how long it will take until I run into some limitation and/or&#xA;strong annoyance from using lockdown mode. If that doesn&amp;rsquo;t ever happen though,&#xA;perhaps it&amp;rsquo;s a good idea to stick to it.&lt;/p&gt;&#xA;&lt;div class=&#34;footnotes&#34; role=&#34;doc-endnotes&#34;&gt;&#xA;&lt;hr&gt;&#xA;&lt;ol&gt;&#xA;&lt;li id=&#34;fn:1&#34;&gt;&#xA;&lt;p&gt;Ironically, not mine, as I &lt;a href=&#34;https://perrotta.dev/2022/02/website-fonts-just-use-the-system-font-stack/&#34;&gt;don&amp;rsquo;t&lt;/a&gt; use any custom&#xA;fonts in this blog.&amp;#160;&lt;a href=&#34;https://perrotta.dev/2025/01/ios-lockdown-mode/#fnref:1&#34; class=&#34;footnote-backref&#34; role=&#34;doc-backlink&#34;&gt;&amp;#x21a9;&amp;#xfe0e;&lt;/a&gt;&lt;/p&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;/div&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: iOS lockdown mode&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/privacy/&#34;&gt;#privacy&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Pin all github actions
      </title>
      <link>https://perrotta.dev/2024/12/pin-all-github-actions/</link>
      <pubDate>Sat, 28 Dec 2024 20:49:27 -0300</pubDate><author>serendipity@perrotta.dev (Thiago Perrotta)</author>
      <category>dev</category>
      <category>pre-commit</category>
      <category>security</category>
      <guid>https://perrotta.dev/2024/12/pin-all-github-actions/</guid>
      <description>&lt;p&gt;♠ &lt;a href=&#34;https://cedwards.xyz/github-actions-are-an-impending-security-disaster/&#34;&gt;via Connor Edwards&lt;/a&gt;:&lt;/p&gt;&#xA;&lt;blockquote&gt;&#xA;&lt;p&gt;Since git tags can be moved arbitrarily by the author, there&amp;rsquo;s nothing stopping them going rogue and changing the Action to be malicious at any time. The author could change the code to exfiltrate the repository itself, the GITHUB_TOKEN (which in many cases can have more power than it should), or other CI/CD secrets.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&#xA;&lt;p&gt;Hence, bad:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;uses: actions/checkout@v4&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Good:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;I find an effective way to address and enforce this practice is via &lt;a href=&#34;https://pre-commit.com/&#34;&gt;pre-commit&lt;/a&gt;:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-yaml&#34;&gt;- repo: https://github.com/lalten/check-gha-pinning&#xA;  rev: v1.2.0 # or whatever is the latest version&#xA;  hooks:&#xA;    - id: check-gha-pinning&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;Try this to check whether there are any existing violations in your repositories:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-bash&#34;&gt;% pre-commit try-repo https://github.com/lalten/check-gha-pinning --all-files&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;I had some in my blog:&lt;/p&gt;&#xA;&#xA;&lt;pre&gt;&lt;code class=&#34;language-plaintext&#34;&gt;GitHub Actions Pinning...................................................Failed&#xA;- hook id: check-gha-pinning&#xA;- exit code: 1&#xA;&#xA;.github/workflows/gh-pages.yml:22: peaceiris/actions-hugo@v3 is not pinned to commit (should be e3b661c523413d13d642651a5ba5fc0d2b344c0d # v3)&#xA;.github/workflows/gh-pages.yml:27: extractions/setup-just@v2 is not pinned to commit (should be dd310ad5a97d8e7b41793f8ef055398d51ad4de6 # v2.0.0)&#xA;.github/workflows/gh-pages.yml:33: peaceiris/actions-gh-pages@v4 is not pinned to commit (should be e9c66a37f080288a11235e32cbe2dc5fb3a679cc # v4)&lt;/code&gt;&lt;/pre&gt;&#xA;&lt;p&gt;They are now fixed.&lt;/p&gt;&#xA;&lt;p&gt;— § —&lt;/p&gt;&lt;p&gt;Reply via &lt;a href=&#34;mailto:serendipity@perrotta.dev?subject=Reply to: Pin all github actions&#34;&gt;email&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href=&#34;https://perrotta.dev/tags/dev/&#34;&gt;#dev&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/pre-commit/&#34;&gt;#pre-commit&lt;/a&gt; &lt;a href=&#34;https://perrotta.dev/tags/security/&#34;&gt;#security&lt;/a&gt;&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
