<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: smac89</title>
    <description>The latest articles on DEV Community by smac89 (@smac89).</description>
    <link>https://dev.to/smac89</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F623609%2F09f08769-9436-4ff8-8357-731c5e60fefc.png</url>
      <title>DEV Community: smac89</title>
      <link>https://dev.to/smac89</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/smac89"/>
    <language>en</language>
    <item>
      <title>Reading saved firefox passwords via cli and other woes</title>
      <dc:creator>smac89</dc:creator>
      <pubDate>Fri, 16 Sep 2022 08:16:23 +0000</pubDate>
      <link>https://dev.to/smac89/reading-saved-firefox-passwords-via-cli-and-other-woes-56ko</link>
      <guid>https://dev.to/smac89/reading-saved-firefox-passwords-via-cli-and-other-woes-56ko</guid>
      <description>&lt;p&gt;I was recently working from home and needed a saved password from Firefox on my dev machine at work. Seeing as the only connection I had with the remote machine was through ssh, this meant the only option I had was to retrieve the password through the &lt;strong&gt;commandline&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I looked online and it wasn't long before I found a tool called &lt;a href="https://github.com/glondu/nss-passwords/tree/0.3" rel="noopener noreferrer"&gt;&lt;code&gt;nss-passwords&lt;/code&gt;&lt;/a&gt;. I &lt;a href="https://packages.ubuntu.com/source/jammy/nss-passwords" rel="noopener noreferrer"&gt;installed&lt;/a&gt; it and gave it a try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ nss-passwords -d ~/.mozilla/firefox/wnhkpui2.dev-edition-default localhost:3001
| http://localhost:3001 | japoduje@mailinator.com   | oRvr2x^4#w8X@sPd |
| http://localhost:3001 | rapakejuqe@mailinator.com | veryxilu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Wow! 🎉
&lt;/h3&gt;

&lt;p&gt;Crazy how it worked soo easily! No need for superuser permissions: I can just read my saved passwords...&lt;/p&gt;

&lt;p&gt;Hold up! Wait a minute! Why was that soo easy?&lt;/p&gt;

&lt;h2&gt;
  
  
  Is this safe?
&lt;/h2&gt;

&lt;p&gt;Well, if you are really curious like I was and don't mind reading a bit of ocaml, the main code is right &lt;a href="https://github.com/glondu/nss-passwords/blob/0.3/main.ml" rel="noopener noreferrer"&gt;here&lt;/a&gt;. It's been a minute since I read OCaml code, but a cursory glance through the code reveals that it looks in your firefox profile folder (the &lt;code&gt;-d&lt;/code&gt; option) to find a file called &lt;code&gt;logins.json&lt;/code&gt; or &lt;code&gt;signons.sqlite&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nn"&gt;Sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;file_exists&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;FilePath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;concat&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;dir&lt;/span&gt; &lt;span class="s2"&gt;"logins.json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
 &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="n"&gt;exec_json&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;
 &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;exec_sqlite&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Here be dragons! 🐉
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;exec_json&lt;/code&gt; function in turn reads the json file, and extracts an array called &lt;code&gt;logins&lt;/code&gt;, which it passes to another function called &lt;code&gt;json_process&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;exec_json&lt;/span&gt; &lt;span class="bp"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="c"&gt;(** I totally
      get all
      of this
  *)&lt;/span&gt;
  &lt;span class="nn"&gt;List&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;iter&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json_process&lt;/span&gt; &lt;span class="n"&gt;logins&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logins&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;queries&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each element of the array looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;104&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"hostname"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://host.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"httpRealm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"formSubmitURL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://host.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"usernameField"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"passwordField"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"password"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"encryptedUsername"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MXXXXPgAAAAAAAAAAAAAAAAAAAEwXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"encryptedPassword"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MXXXXPgAAAAAAAAAAAAAAAAAAAEwXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"guid"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{c0f43272-22a3-43db-b5bd-2d0cfbe621dd}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"encType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"timeCreated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1662743548172&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"timeLastUsed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1662743548172&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"timePasswordChanged"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1662743548172&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"timesUsed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&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;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, I decided to attempt to replicate what the code is doing using just command-line. &lt;em&gt;The reason being to see how easy it would be for some random &lt;code&gt;npm&lt;/code&gt; package you download to &lt;a href="https://docs.npmjs.com/cli/v8/using-npm/scripts#life-cycle-scripts" rel="noopener noreferrer"&gt;run a simple command&lt;/a&gt; to extract your passwords&lt;/em&gt;, so this one liner gives us the same json array we get from the above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find &lt;span class="nt"&gt;-L&lt;/span&gt; ~/.mozilla/firefox/wnhkpui2.dev-edition-default &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s1"&gt;'logins.json'&lt;/span&gt; &lt;span class="nt"&gt;-exec&lt;/span&gt; jq &lt;span class="s1"&gt;'.logins'&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Continuing... 🕳️
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;json_process&lt;/code&gt; function doesn't seem all too interesting. However, it calls another function called &lt;code&gt;do_decrypt&lt;/code&gt;, which is declared as the following in OCaml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ocaml"&gt;&lt;code&gt;&lt;span class="k"&gt;external&lt;/span&gt; &lt;span class="n"&gt;do_decrypt&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"caml_do_decrypt"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That looks like a &lt;a href="https://en.wikipedia.org/wiki/Foreign_function_interface" rel="noopener noreferrer"&gt;ffi&lt;/a&gt; for calling a function from another language, and in this case it looks like the &lt;a href="https://github.com/glondu/nss-passwords/blob/0.3/nss_stubs.c#L98" rel="noopener noreferrer"&gt;function&lt;/a&gt; is written in C:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;CAMLprim&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="nf"&gt;caml_do_decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;CAMLparam2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;CAMLlocal3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cb_data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;dataString&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;String_val&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;strLen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;caml_string_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;SECItem&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;decoded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;NSSBase64_DecodeBuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dataString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strLen&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;SECStatus&lt;/span&gt; &lt;span class="n"&gt;rv&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;SECItem&lt;/span&gt;    &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;siBuffer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;decoded&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decoded&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* Base64 decoding failed */&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Val_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PORT_GetError&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decoded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;SECITEM_FreeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decoded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PR_TRUE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
      &lt;span class="n"&gt;caml_raise_with_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;caml_named_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NSS_base64_decode_failed"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="cm"&gt;/* Base64 decoding succeeded */&lt;/span&gt;
  &lt;span class="cm"&gt;/* Build the argument to password_func ((bool -&amp;gt; string) * exn option) */&lt;/span&gt;
  &lt;span class="n"&gt;cb_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;caml_alloc_tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Store_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;Store_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Val_unit&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="cm"&gt;/* None */&lt;/span&gt;
  &lt;span class="cm"&gt;/* Decrypt */&lt;/span&gt;
  &lt;span class="n"&gt;rv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PK11SDR_Decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decoded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cb_data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;SECITEM_ZfreeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decoded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PR_TRUE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rv&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;SECSuccess&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;caml_alloc_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;memcpy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bytes_val&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;SECITEM_ZfreeItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;PR_FALSE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;CAMLreturn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="cm"&gt;/* decryption failed */&lt;/span&gt;
  &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Val_int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;PORT_GetError&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="n"&gt;exn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cb_data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exn&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="n"&gt;caml_raise_with_args&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;caml_named_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"NSS_decrypt_failed"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks like the first thing it does is to attempt to use &lt;code&gt;base64&lt;/code&gt; to decode the encrypted value...ok.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;rv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;PK11SDR_Decrypt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;decoded&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;cb_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next it is calling this &lt;code&gt;PK11SDR_Decrypt&lt;/code&gt; function, which seems to be part of the &lt;a href="https://github.com/nss-dev/nss" rel="noopener noreferrer"&gt;&lt;code&gt;nss&lt;/code&gt;&lt;/a&gt; library.&lt;/p&gt;

&lt;p&gt;Well I don't have time to start digging into how all that works, but it turns out we can download a package called &lt;a href="https://packages.ubuntu.com/jammy/libnss3-tools" rel="noopener noreferrer"&gt;nss-tools&lt;/a&gt;, which contains a command for decrypting the encrypted values, called &lt;code&gt;pwdecrypt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Extending our commandline above, we can successfully decrypt the username and password for any given domain by doing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find &lt;span class="nt"&gt;-L&lt;/span&gt; ~/.mozilla/firefox/wnhkpui2.dev-edition-default &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s1"&gt;'logins.json'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-execdir&lt;/span&gt; sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'jq '&lt;/span&gt;&lt;span class="s2"&gt;"'"&lt;/span&gt;&lt;span class="s1"&gt;'.logins | .[] | select(.hostname | endswith("host.com")) | "\(.encryptedUsername)\n\(.encryptedPassword)"'&lt;/span&gt;&lt;span class="s2"&gt;"'"&lt;/span&gt;&lt;span class="s1"&gt;' -r "$1" | pwdecrypt -d .'&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Replace &lt;code&gt;host.com&lt;/code&gt; from the above command with an actual hostname and it will spit out the username followed by the password&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Woah! ✋🏼
&lt;/h2&gt;

&lt;p&gt;What a bittersweet ending. On one hand, I've just discovered a way to read my firefox saved passwords, on the other hand I've just discovered that literally any npm package I install, can run the same command to extract my saved passwords. Thankfully I don't save real passwords on Firefox, I use bitwarden password manager, and only passwords I save on FF are passwords I use for logging into dummy test accounts.&lt;/p&gt;

&lt;p&gt;I'm also a bit confused as to why it is &lt;a href="https://firefox-source-docs.mozilla.org/security/nss/index.html" rel="noopener noreferrer"&gt;Mozilla&lt;/a&gt; who is building the &lt;a href="https://github.com/nss-dev/nss" rel="noopener noreferrer"&gt;libraries&lt;/a&gt; and &lt;a href="https://github.com/mozilla/nss-tools" rel="noopener noreferrer"&gt;tools&lt;/a&gt; that enables this ease of access...&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Btw if you think this is a firefox issue, think again. A quick search online reveals there are tools available for &lt;a href="https://github.com/hassaanaliw/chromepass" rel="noopener noreferrer"&gt;Chrome&lt;/a&gt; (which will most likely work on Brave, Edge, and Chromium). Someone also wrote an entire &lt;a href="https://kylemistele.medium.com/stealing-saved-browser-passwords-your-new-favorite-post-exploitation-technique-c5e72c86159a" rel="noopener noreferrer"&gt;medium article&lt;/a&gt; detailing methods of "extracting" passwords from all major browsers.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  There might be hope...💡
&lt;/h3&gt;

&lt;p&gt;I haven't been able to test this, but perhaps the reason it is so easy to extract the passwords is because I haven't set a master/primary password in Firefox? 🤔&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6y739clh98tava4nx4ah.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6y739clh98tava4nx4ah.png" alt="Firefox settings" width="800" height="80"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Update Oct 3rd, 2022
&lt;/h2&gt;

&lt;p&gt;Indeed it turns out that if you set a password, then attempting to use any of the above methods will result in a password prompt:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using &lt;code&gt;nss-passwords&lt;/code&gt; script:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fusbugeqd71go029rgztl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fusbugeqd71go029rgztl.png" alt="Gnome pinentry" width="403" height="284"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using the command-line option:&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxbww87z5hfczqp4az3d.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxbww87z5hfczqp4az3d.gif" alt="Commandline password prompt" width="800" height="77"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Lessons learned
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Never store passwords on your browser&lt;/li&gt;
&lt;li&gt;Use a dedicated password manager like IPassword, &lt;a href="https://bitwarden.com/" rel="noopener noreferrer"&gt;Bitwarden&lt;/a&gt;, Lastpass, etc&lt;/li&gt;
&lt;li&gt;If you must store the password in the browser, then make sure to use the master password option if your browser provides one.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>firefox</category>
      <category>commandline</category>
      <category>security</category>
      <category>password</category>
    </item>
    <item>
      <title>Better way to try...except...pass in Python</title>
      <dc:creator>smac89</dc:creator>
      <pubDate>Thu, 23 Jun 2022 04:31:00 +0000</pubDate>
      <link>https://dev.to/smac89/better-way-to-tryexceptpass-in-python-2460</link>
      <guid>https://dev.to/smac89/better-way-to-tryexceptpass-in-python-2460</guid>
      <description>&lt;p&gt;The typical '&lt;em&gt;try, exception, ignore&lt;/em&gt;' pattern I see people using in python is this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="c1"&gt;# do something that might raise
&lt;/span&gt;&lt;span class="nf"&gt;except &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyError1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MyError2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
  &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you've ever found yourself doing something like that, I'm &lt;strong&gt;not&lt;/strong&gt; here to tell you to &lt;em&gt;stop&lt;/em&gt;, instead I'll show you a better way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introducing &lt;a href="https://docs.python.org/3/library/contextlib.html#contextlib.suppress" rel="noopener noreferrer"&gt;&lt;code&gt;contextlib.suppress&lt;/code&gt;&lt;/a&gt; builtin
&lt;/h3&gt;

&lt;p&gt;Using &lt;code&gt;suppress&lt;/code&gt;, we can transform the above syntax to this:&lt;br&gt;
&lt;strong&gt;&lt;em&gt;requires python 3.4+&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;suppress&lt;/span&gt;
&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nf"&gt;suppress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyError1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MyError2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;etc&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="c1"&gt;# do something that might raise
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there we have it, from 4 lines to a nicely condensed 2 lines.&lt;/p&gt;

&lt;h4&gt;
  
  
  Advantages
&lt;/h4&gt;

&lt;p&gt;What do you gain from this? Shorter, concise code which makes clear the intentions of the user to ignore the exceptions.&lt;/p&gt;

&lt;h4&gt;
  
  
  Disadvantages
&lt;/h4&gt;

&lt;p&gt;The exception will never be visible outside of the context, therefore if you are being a good log keeper and in the habit of using &lt;code&gt;logger.exception("I tried to do something")&lt;/code&gt;, the exception will not be shown by the logger.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;If you are already using this pattern, then I'm assuming you know what you're doing. The disadvantage mentioned might be a blocker for some. Consult your nearest python guru if you experience any adverse effects.&lt;/p&gt;

</description>
      <category>python</category>
      <category>exceptions</category>
      <category>programming</category>
    </item>
    <item>
      <title>Overloading vs Overriding in Typescript</title>
      <dc:creator>smac89</dc:creator>
      <pubDate>Fri, 13 May 2022 17:17:22 +0000</pubDate>
      <link>https://dev.to/smac89/overloading-vs-overriding-in-typescript-16cl</link>
      <guid>https://dev.to/smac89/overloading-vs-overriding-in-typescript-16cl</guid>
      <description>&lt;p&gt;The main difference between overloading and overriding is this: The former is concerned with the signature of a function, whereas the later is concerned with the behaviour of a method.&lt;/p&gt;

&lt;h2&gt;
  
  
  Override
&lt;/h2&gt;

&lt;p&gt;Override is something you do to change the &lt;em&gt;behaviour&lt;/em&gt; of a method in a &lt;em&gt;subclass&lt;/em&gt;. When you override a method, it is expected that you intend to change the default behaviour of the method, that was inherited from the parent class. However, the method signature should remain the same in order not to violate &lt;a href="https://en.wikipedia.org/wiki/Liskov_substitution_principle" rel="noopener noreferrer"&gt;LSP&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Overload
&lt;/h2&gt;

&lt;p&gt;Overload is something you do to functions; Not to change their behaviour*, but rather to diversify their &lt;a href="https://en.wikipedia.org/wiki/Type_signature" rel="noopener noreferrer"&gt;signature&lt;/a&gt;. A function overload keeps the same name as the function, but it's free to introduce a different signature. You've probably seen an example of this if you come from a JavaScript background and seen &lt;a href="https://lodash.com/docs/#reduce" rel="noopener noreferrer"&gt;functions&lt;/a&gt; which have parameters that can be one of two or more types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
* @param {string|string[]} The object to use
*/&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;stringOrArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strOrArr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's difficult to tell that this is a function overload because JavaScript does not have a strong enough type system to disambiguate the overloads. However, with the &lt;em&gt;Jsdoc&lt;/em&gt; comment, we can sort of make out that this function has an overload.&lt;br&gt;
With typescript we can make this overload an actual part of our function signature, instead of living in a comment.&lt;/p&gt;

&lt;p&gt;ex.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;stringOrArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;stringOrArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;stringOrArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;strOrArr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The implementation of the &lt;em&gt;overloaded&lt;/em&gt; function stays the same, but the signature is allowed to be different. In the case of &lt;em&gt;overriding&lt;/em&gt;, the implementation can be different, but the signature has to be the same, in order not to violate the Liskov Substitution Principle.&lt;/p&gt;

</description>
      <category>inheritance</category>
      <category>overload</category>
      <category>override</category>
      <category>typescript</category>
    </item>
    <item>
      <title>cURL docker through sockets</title>
      <dc:creator>smac89</dc:creator>
      <pubDate>Thu, 17 Feb 2022 08:57:06 +0000</pubDate>
      <link>https://dev.to/smac89/curl-to-docker-through-sockets-1mhe</link>
      <guid>https://dev.to/smac89/curl-to-docker-through-sockets-1mhe</guid>
      <description>&lt;p&gt;&lt;em&gt;Did you know that the docker daemon exposes a &lt;a href="https://grpc.io/" rel="noopener noreferrer"&gt;gRPC&lt;/a&gt; api for communication OR that cURL can send data to unix sockets?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's see how this all works.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker engine
&lt;/h3&gt;

&lt;p&gt;The underlying infrastructure of the docker ecosystem is the &lt;a href="https://docs.docker.com/engine/" rel="noopener noreferrer"&gt;docker engine&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It is implemented using a client/server model (much like most unix tools are), and exposes its &lt;a href="https://docs.docker.com/engine/api/" rel="noopener noreferrer"&gt;API&lt;/a&gt; over a gRPC interface. As usual in such model, there is usually only one server, and many clients, and the story is no different for docker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdocs.docker.com%2Fengine%2Fimages%2Farchitecture.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdocs.docker.com%2Fengine%2Fimages%2Farchitecture.svg" alt="Docker architecture image" width="800" height="400"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Docker architecture. Image from &lt;a href="https://docs.docker.com/get-started/overview/#docker-architecture" rel="noopener noreferrer"&gt;https://docs.docker.com&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The daemon acts as the &lt;strong&gt;server&lt;/strong&gt;, while the CLI which we are all familiar with acts as one of the &lt;strong&gt;clients&lt;/strong&gt;. The single point of communication is usually exposed as a unix socket, and communication is carried out via gRPC exchanging data in the form of &lt;a href="https://developers.google.com/protocol-buffers" rel="noopener noreferrer"&gt;protocol buffers&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  cURL --unix-socket
&lt;/h3&gt;

&lt;p&gt;It turns out that cURL is not just great for talking to HTTP servers, but it can apparently also do the same via UNIX SOCKETS.&lt;/p&gt;

&lt;p&gt;Let's say we wanted to get the docker version currently installed; Normally you would do the following on the command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using the api, we do something like&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl --silent --unix-socket /run/user/1000/docker.sock http://v1.41/version
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;sup&gt;&lt;em&gt;Note the above socket path assumes you are running docker in rootless mode and your user id is &lt;code&gt;1000&lt;/code&gt;. If running docker as root, the socket is likely to be located at &lt;code&gt;/var/run/docker.sock&lt;/code&gt;&lt;/em&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Which gives us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{"Platform":{"Name":"Docker Engine - Community"},"Components":[{"Name":"Engine","Version":"20.10.12","Details":{"ApiVersion":"1.41","Arch":"amd64","BuildTime":"2021-12-13T11:46:12.000000000+00:00","Experimental":"false","GitCommit":"459d0df","GoVersion":"go1.16.12","KernelVersion":"5.13.0-28-generic","MinAPIVersion":"1.12","Os":"linux"}},{"Name":"containerd","Version":"v1.4.12","Details":{"GitCommit":"7b11cfaabd73bb80907dd23182b9347b4245eb5d"}},{"Name":"runc","Version":"1.0.2","Details":{"GitCommit":"v1.0.2-0-g52b36a2d"}},{"Name":"docker-init","Version":"0.19.0","Details":{"GitCommit":"de40ad0"}}],"Version":"20.10.12","ApiVersion":"1.41","MinAPIVersion":"1.12","GitCommit":"459d0df","GoVersion":"go1.16.12","Os":"linux","Arch":"amd64","KernelVersion":"5.13.0-28-generic","BuildTime":"2021-12-13T11:46:12.000000000+00:00"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How cool is that?!
&lt;/h3&gt;

&lt;p&gt;You can take this even a step further, and expose a local docker daemon, over tcp to your friends over the internet. Here is a quick example which doesn't use cURL, but rather uses &lt;a href="https://man7.org/linux/man-pages/man1/ncat.1.html" rel="noopener noreferrer"&gt;ncat&lt;/a&gt;:&lt;/p&gt;

&lt;h4&gt;
  
  
  server
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ncat -kl -p 2376 -c 'ncat -U /run/user/1000/docker.sock'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  client
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl http://localhost:2376/version | jq
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;One last thing to mention is that the format of the url ex. &lt;code&gt;http://v1.41/version&lt;/code&gt; is not strict. Any of the following would have given the same result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;http:/foo/version&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http://dummy/version&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;http:/./version&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://docs.docker.com/engine/api/#versioned-api-and-sdk" rel="noopener noreferrer"&gt;recommendation&lt;/a&gt; is to use the &lt;strong&gt;version of the API&lt;/strong&gt; supported by docker as the first part of the url, then the actual commands come later.&lt;/p&gt;




&lt;p&gt;Sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;v1.41 &lt;a href="https://docs.docker.com/engine/api/v1.41/" rel="noopener noreferrer"&gt;api specification&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nathanleclaire.com/blog/2015/11/12/using-curl-and-the-unix-socket-to-talk-to-the-docker-api/" rel="noopener noreferrer"&gt;https://nathanleclaire.com/blog/2015/11/12/using-curl-and-the-unix-socket-to-talk-to-the-docker-api/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>docker</category>
      <category>curl</category>
      <category>api</category>
      <category>serverclient</category>
    </item>
    <item>
      <title>Custom ports in SSH config for VSCode Remote SSH</title>
      <dc:creator>smac89</dc:creator>
      <pubDate>Fri, 28 Jan 2022 17:33:52 +0000</pubDate>
      <link>https://dev.to/smac89/custom-ports-in-ssh-config-for-vscode-remote-ssh-505c</link>
      <guid>https://dev.to/smac89/custom-ports-in-ssh-config-for-vscode-remote-ssh-505c</guid>
      <description>&lt;h2&gt;
  
  
  Scenario
&lt;/h2&gt;

&lt;p&gt;You want to connect to a remote workstation via VsCode, but on a different port than the default (22).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why would you need to do this?&lt;/strong&gt;&lt;br&gt;
There could be any reason, but one that comes to mind is if  the default port requires authentication using something like PAM, or Kerberos, and you only want to be bothered by entering passwords when you use the default port instead of a custom port.&lt;/p&gt;
&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;By default the &lt;a href="https://github.com/Microsoft/vscode-remote-release.git" rel="noopener noreferrer"&gt;Vscode Remote SSH&lt;/a&gt; plugin uses your local &lt;code&gt;~/.ssh/config&lt;/code&gt; file for configuring remote connections. Let's assume that in that file, you have created a host configuration like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host my-company-machine
   User my-user-name
   HostName 127.0.0.1
   GSSAPIAuthentication yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you would like to connect to the remote machine through VSCode's Remote SSH extension, but using a different port than the default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ok solution
&lt;/h2&gt;

&lt;p&gt;The easy solution would be to just add the port to the config file like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host my-company-machine
   User my-user-name
   HostName 127.0.0.1
   GSSAPIAuthentication no
   Port 5000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now VScode can connect to &lt;code&gt;my-company-name&lt;/code&gt; using port 5000 on the remote, and problem solved?&lt;/p&gt;

&lt;p&gt;Not really because we've now removed the authentication method required for the default port.&lt;/p&gt;

&lt;h2&gt;
  
  
  Better solution
&lt;/h2&gt;

&lt;p&gt;The manpage for &lt;a href="https://man7.org/linux/man-pages/man5/ssh_config.5.html" rel="noopener noreferrer"&gt;&lt;code&gt;ssh_config(5)&lt;/code&gt;&lt;/a&gt; lists a directive called &lt;code&gt;Include&lt;/code&gt;, which allows us to include other ssh configs inside ours.&lt;/p&gt;

&lt;p&gt;Furthermore, we can &lt;strong&gt;override&lt;/strong&gt; configuration for host declarations found in those other configs.&lt;/p&gt;

&lt;p&gt;The solution I propose (and which has worked for me), is to create a new config for vscode, and in that file, override the port used to connect to the remote host.&lt;/p&gt;

&lt;h3&gt;
  
  
  ~/.ssh/config
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host my-company-machine
   User my-user-name
   HostName 127.0.0.1
   GSSAPIAuthentication yes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ~/.ssh/vscode-config
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host my-company-machine
   GSSAPIAuthentication no
   Port 5000
Include ~/.ssh/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you just need to change your plugin settings so that VSCode uses this new config, instead of your default one. Next time you connect to the remote host, VSCode will use port &lt;code&gt;5000&lt;/code&gt;, and when you connect via regular ssh from a terminal, you will use port &lt;code&gt;22&lt;/code&gt; by default, (&lt;em&gt;or specify a custom port via the &lt;code&gt;-p&lt;/code&gt; option&lt;/em&gt;).&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>ssh</category>
      <category>remote</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Forcing webpack to recompile your files</title>
      <dc:creator>smac89</dc:creator>
      <pubDate>Tue, 18 Jan 2022 04:09:56 +0000</pubDate>
      <link>https://dev.to/smac89/quickest-method-to-cause-webpack-to-recompile-your-file-1l4e</link>
      <guid>https://dev.to/smac89/quickest-method-to-cause-webpack-to-recompile-your-file-1l4e</guid>
      <description>&lt;p&gt;Just today I started noticing that when my react code rebuilds, I get eslint errors in the console, but not in my IDE.&lt;/p&gt;

&lt;p&gt;Even stranger was the fact that when I run &lt;code&gt;eslint&lt;/code&gt; by itself in the commandline, it doesn't show that anything was wrong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;eslint --cache --format stylish --ext '.js,.jsx,.ts,.tsx' --quiet ./
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What gives?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbdnagtgftohaq06ntrui.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbdnagtgftohaq06ntrui.gif" alt="Come On Reaction GIF" width="480" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The solution
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;touch&lt;/code&gt; command to the rescue. In order to force webpack to recompile the files, they had to be &lt;em&gt;changed&lt;/em&gt; somehow, and I wasn't prepared to manually change each file by hand, recompile, and revert the change, just to wait for another recompilation.&lt;/p&gt;

&lt;p&gt;I simply used the &lt;code&gt;touch&lt;/code&gt; command on Linux to "touch" all the files which were having that problem, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;touch src/pages/**/*.{js,jsx,tsx,ts}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;shell is zsh&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After running this command, webpack was forced to recompile everything and I no longer saw those pesky errors again.&lt;/p&gt;

</description>
      <category>webpack</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
    </item>
    <item>
      <title>Restore Windows UEFI boot</title>
      <dc:creator>smac89</dc:creator>
      <pubDate>Mon, 06 Dec 2021 06:21:03 +0000</pubDate>
      <link>https://dev.to/smac89/restore-windows-uefi-boot-31lo</link>
      <guid>https://dev.to/smac89/restore-windows-uefi-boot-31lo</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Sometime last week, I decided to move my entire ESP folder into a dedicated boot partition mounted at &lt;code&gt;/boot&lt;/code&gt;. (I did this because I liked the idea of having everything boot-related in a partition for easy access when I dual boot or need any file from there). Unfortunately, I didn't attempt to reboot in order to check if anything was broken...until today&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Windows boot is broken...
&lt;/h2&gt;

&lt;p&gt;And by "broken", I mean that in the rEFInd boot menu, there was nothing showing for windows. It only showed my linux installation.&lt;/p&gt;

&lt;p&gt;At first, I was sure this was an easy fix and all I had to do was boot from a linux disk image, and run the commands:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjifat8bn0djm494ujghw.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjifat8bn0djm494ujghw.jpg" alt="hand holding wrench" width="800" height="1169"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mount /dev/sda3 /mnt
mount /dev/sda1 /mnt/boot
refind-install --root /mnt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...unfortunately, that didn't work :(. Attempting to boot directly from &lt;code&gt;Windows Manager&lt;/code&gt; also didn't work, so at this point I knew something had gone very wrong.&lt;/p&gt;

&lt;p&gt;As my sunday evening grew into sunday night, I decided to actually stop being lazy with this fix, and discover why windows was being soo elusive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps to recovery
&lt;/h2&gt;

&lt;p&gt;Using the steps outlined in the &lt;a href="https://wiki.archlinux.org/title/REFInd#UEFI_shell" rel="noopener noreferrer"&gt;arch wiki&lt;/a&gt; for rEFInd, I was able to get my hands on an EFI shell. Using some of the commands from the arch wiki, I was able to discover that the windows boot EFI file didn't exist anymore in the boot partition.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8f7yw0qqp5ofy51qb7x6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8f7yw0qqp5ofy51qb7x6.gif" alt="output of the command bcfg boot dump -v" width="298" height="168"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;output of &lt;code&gt;bcfg boot dump -v&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Well that explains a lot!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Both rEFInd and Windows boot manager expected to find a &lt;code&gt;\EFI\Microsoft\Boot\bootmgfw.efi&lt;/code&gt; file, but it wasn't finding it, so the boot never worked.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1. Add &lt;code&gt;bootmgfw.efi&lt;/code&gt; to rEFInd
&lt;/h3&gt;

&lt;p&gt;Boot into Linux and mount your windows partition, then copy the existing file into where rEFInd was expecting to find it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo mkdir -p /boot/EFI/Microsoft/Boot
sudo cp /run/media/smac89/Windows/Windows/Boot/EFI/bootmgfw.efi /boot/EFI/Microsoft/Boot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;At this point, you will see the blue windows logo from the rEFInd boot menu, but attempting to boot from it may result in the following screen:&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fieppjw0b82nv4ubelm6i.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fieppjw0b82nv4ubelm6i.jpg" alt="Windows failed to start" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;...if so, continue to the next step&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Get access to a windows terminal
&lt;/h3&gt;

&lt;p&gt;There are &lt;a href="https://www.laptopmag.com/articles/repair-windows-10" rel="noopener noreferrer"&gt;many&lt;/a&gt; ways to do &lt;a href="https://support.microsoft.com/en-us/windows/create-installation-media-for-windows-99a58364-8c02-206f-aa6f-40c3b507420d" rel="noopener noreferrer"&gt;this&lt;/a&gt;. Pick the one that is most convenient, and proceed.&lt;/p&gt;

&lt;p&gt;Inside the terminal type the commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;diskpart
list volumes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Find the volume containing the boot partition, and assign it a label if it doesn't have one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;select volume N
assign letter=m
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exit &lt;code&gt;diskpart&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we use the following command to install windows boot entries to the boot partition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bcdboot C:\Windows /l en-US /s m: /f ALL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running the above command, the boot partition now contains all the entries needed to boot windows.&lt;/p&gt;

&lt;p&gt;Reboot.&lt;/p&gt;




&lt;p&gt;Thanks for reading.&lt;/p&gt;

</description>
      <category>efi</category>
      <category>linux</category>
      <category>bootmgr</category>
      <category>refind</category>
    </item>
  </channel>
</rss>
