<?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>Forem: Rida F'kih</title>
    <description>The latest articles on Forem by Rida F'kih (@rida).</description>
    <link>https://forem.com/rida</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%2F846394%2Fd03030ec-4398-4c3f-9a1b-2546a8c10190.png</url>
      <title>Forem: Rida F'kih</title>
      <link>https://forem.com/rida</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/rida"/>
    <language>en</language>
    <item>
      <title>'Nothing Chats' is Not Secure.</title>
      <dc:creator>Rida F'kih</dc:creator>
      <pubDate>Sat, 18 Nov 2023 08:30:42 +0000</pubDate>
      <link>https://forem.com/rida/nothing-chats-is-not-secure-5a1i</link>
      <guid>https://forem.com/rida/nothing-chats-is-not-secure-5a1i</guid>
      <description>&lt;h2&gt;
  
  
  Contributors
&lt;/h2&gt;

&lt;p&gt;These findings were discovered in a joint effort between &lt;a href="https://twitter.com/batuhan"&gt;Batuhan İçöz&lt;/a&gt;, &lt;a href="https://twitter.com/1ConanEdogawa"&gt;1Conan&lt;/a&gt;, and I. Follow them on Twitter, they are both extremely talented.&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;On November 16th, 2023, alongside an announcement by MKBHD, Nothing announced ‘Nothing Chats,’ an application exclusive to their hardware which brings iMessage to Android in a partnership with Sunbird. ‘Nothing Chats’ is a reskinned version of the existing Sunbird application, currently available on the Google Play Store.&lt;/p&gt;

&lt;p&gt;After seeing conflicting statements related to the security of their application, members of the Texts.com reverse engineering team decided to take a look into the Sunbird application and their security practices.&lt;/p&gt;

&lt;p&gt;‘Sunbird’ and consequently the ‘Nothing Chats’ application require sending your Apple ID credentials to their servers, where they authenticate on your behalf using a virtual machine running MacOS. If you’re an Apple user, this is the same Apple ID which you use to access your notes, photos, iCloud storage, email, and more. Preliminary findings were tested against the ‘Sunbird’ application, but we used the official ‘Nothing Chats’ application to confirm these vulnerabilities affected Nothing’s version as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notification
&lt;/h2&gt;

&lt;p&gt;Immediately, the Texts.com reverse engineering team noticed a few vulnerabilities and implementation issues which Kishan briefly outlined on Twitter / X. The main issue outlined being a vital request containing important credentials happening over an unencrypted channel (HTTP)&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1725586252630540555-333" src="https://platform.twitter.com/embed/Tweet.html?id=1725586252630540555"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1725586252630540555-333');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1725586252630540555&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Sunbird’s Response
&lt;/h2&gt;

&lt;p&gt;Sunbird responded by denying any vulnerabilities, and justifying their implementation, doubling down on it being secure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CV0U3Ooa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/furxx6p74dten906n54s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CV0U3Ooa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/furxx6p74dten906n54s.png" alt="tweets from sunbird justifying their implementation" width="745" height="689"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In short, they made a few claims.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sunbird has ISO27001 certification, which testifies to their commitment towards security.&lt;/li&gt;
&lt;li&gt;The HTTP request which as subject of concern is only a one-off request to notify of an iMessage connection, which then takes place on a secure channel.&lt;/li&gt;
&lt;li&gt;The data is encrypted before being sent over HTTP with a key provided over HTTPS.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Whether or not they are using another service behind the scenes is impossible to tell, and they may be telling the truth that this is just a naming conflict between the pre-existing BlueBubbles service and their own internal service.&lt;/p&gt;

&lt;p&gt;ISO27001 in this case is irrelevant. While its good to be committed to privacy and security, execution matters.&lt;/p&gt;

&lt;p&gt;Other points of the response simply display a misunderstanding of the functionality of the technologies they’re leveraging, the primary one being JWT (JSON Web Tokens). JWTs are signed, not encrypted. Their payloads are accessible, and they themselves act as an access token.&lt;/p&gt;

&lt;p&gt;In this case, the JWT is used to authenticate a user into the realtime Firebase database. It allows them to access storage which includes their account information, messages, accounts / connections, attachments, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Vulnerabilities
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Data in Transit Vulnerability
&lt;/h3&gt;

&lt;p&gt;While Sunbird’s claim that they generate and send the JWT over a secured channel are true, the application immediately turns around and sends the JWT back to another Sunbird service hosted on a load-balanced Express server which does not implement SSL, so requests can be easily intercepted by an attacker.&lt;/p&gt;

&lt;p&gt;The endpoint in question can be found at &lt;code&gt;http://monarch.sunbirdapp.com:8888/register&lt;/code&gt; and accepts two fields in a JSON body. &lt;code&gt;name&lt;/code&gt; which contains our Apple ID, and &lt;code&gt;token&lt;/code&gt; which contains our JWT.&lt;/p&gt;

&lt;p&gt;Transmitting our JWT over an insecure channel is very dangerous, because it acts as an API token which we can use to access all our data. By nature, JWTs cannot be easily invalidated on the server side. If an attacker gets their hands on it, they have unfettered access to the resource it grants until token expiry. In this case, all our account details, messages, attachments, etc., all in realtime.&lt;/p&gt;

&lt;p&gt;By not implementing SSL, we’ve compromised level 7 of our OSI model. If an attacker compromises any point along our network pipeline between the application and the aforementioned Express server, our JWT can become compromised and an attacker will gain access to the information we’ve entrusted to Sunbird / Nothing Chats.&lt;/p&gt;

&lt;p&gt;Attacks can be user-targeted, if you’re on a non-WPA network, a WPA network with a cracked PSK, or a compromised network hosted by an attacker, they can easily steal our packets, and your JWT.&lt;/p&gt;

&lt;p&gt;The real danger begins as we walk down the network pipeline. Depending on the implementation of the load balancer, Express server, or encompassing network, an attacker targeting the server-end could gain access to any and all users who authenticate into iMessage once their attack begins.&lt;/p&gt;

&lt;p&gt;You can see a screenshot of us intercepting a JWT sent over HTTP in the next section as part of a greater attack demonstration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data at Rest Vulnerability
&lt;/h3&gt;

&lt;p&gt;Sunbird does try to implement E2EE, although their implementation is overshadowed by decrypting, and then storing the unencrypted payloads in their database.&lt;/p&gt;

&lt;p&gt;When a message or an attachment is received by a user, they are unencrypted on the server side until the client sends a request acknowledging, and deleting them from the database. This means that an attacker subscribed to the Firebase realtime database will always be able to access the messages before or at the moment they are read by the user.&lt;/p&gt;

&lt;p&gt;In the following screenshot, we’ve intercepted the JWT. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q237NIAC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t2r6jy2w3zxxw7tj397r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q237NIAC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t2r6jy2w3zxxw7tj397r.png" alt="screenshot of reverse engineering process" width="800" height="518"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We then take it, and authenticate into our Firebase realtime database. This subscribes us to changes that occur in this &lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--V03n8ZGG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uvhxa81cua36etjkbq48.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--V03n8ZGG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uvhxa81cua36etjkbq48.png" alt="screenshot of reverse engineering process" width="800" height="740"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this point, with nothing but the JWT, we could easily create a script which could download all information regarding the user and all their conversations with just 23 lines of code.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;To demonstrate this, I sent myself the following message from my real iPhone to an account linked with ‘Nothing Chats’, and then requested the relevant data from Sunbird by running the script. You can see it is visible in plaintext, sans-encryption.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ezewX9VM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ns9s1qp1fzc3k6ui2zn2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ezewX9VM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ns9s1qp1fzc3k6ui2zn2.png" alt="screenshot of reverse engineering process" width="800" height="716"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Insider Threat / Data Exposure
&lt;/h3&gt;

&lt;p&gt;When you send a message using Sunbird or Nothing Chats, the data relating to your message including the contact information, message contents, and attachment URLs are sent to the Sunbird’s Sentry (a debugging platform), which can then be viewed by authorised parties within the company. This contradicts the FAQ item sourced directly from the Nothing website as of November 17th, 2023.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Are my messages secure?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes, Nothing Chats is built on Sunbird’s platform and all Chats messages are end-to-end encrypted, meaning neither we nor Sunbird can access the messages you’re sending and receiving.&lt;/p&gt;

&lt;blockquote&gt;
&lt;/blockquote&gt;

&lt;p&gt;By sending unencrypted outgoing messages to Sentry, authorised individuals at Sunbird would be able to view them from their Sentry dashboard. This makes them susceptible to insider threats. Here is the text content of a request sent to Sentry from the official ‘Nothing Chats’ application. The entire payload includes more messages, and information about the sender and recipient including their contact information.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N1DPDneV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1iqznn4r1alhne8hwoxf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N1DPDneV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1iqznn4r1alhne8hwoxf.png" alt="screenshot of reverse engineering process" width="800" height="301"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Is It Real?
&lt;/h2&gt;

&lt;p&gt;If you’re a ‘Nothing Chats’ user and are viewing this around November 17th, 2023, you may be able to see it with your own eyes. Within a matter of minutes, Batuhan İçöz created an open-source proof of concept which allows you to observe the lack of E2EE in your own browser. &lt;/p&gt;

&lt;p&gt;Simply go to &lt;a href="https://github.com/batuhan/sunbird-poc/tree/main"&gt;batuhan/sunbird-poc&lt;/a&gt;, and once you have a good understanding of the code, visit the URL listed on the repository sidebar. Authenticate into your ‘Nothing Chats’ account. Send yourself some texts, and observe your account details. It’s important to note this repository contains no decryption methods, so if E2EE was implemented, you wouldn’t be able to see your account and text information.&lt;/p&gt;

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

&lt;p&gt;Sending your credentials to third-party services always presents a significant risk. It’s always important to stay vigilant with your information, and consider the security implications of sharing any. By sending our Apple ID to a third-party service, we are not only trusting the third-party with our texts, but should they become compromised, our photos, videos, contacts, notes, keychain, and more.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>programming</category>
      <category>typescript</category>
      <category>startup</category>
    </item>
    <item>
      <title>I Re-Wrote These 10+ Single Lines of JavaScript Code, the Team Lead Praised the Code for Being Elegant</title>
      <dc:creator>Rida F'kih</dc:creator>
      <pubDate>Thu, 20 Oct 2022 04:58:25 +0000</pubDate>
      <link>https://forem.com/rida/i-re-wrote-these-10-single-lines-of-javascript-code-the-team-lead-praised-the-code-for-being-elegant-ec</link>
      <guid>https://forem.com/rida/i-re-wrote-these-10-single-lines-of-javascript-code-the-team-lead-praised-the-code-for-being-elegant-ec</guid>
      <description>&lt;p&gt;I am the Team Lead, I praised the code. /s&lt;/p&gt;

&lt;p&gt;This post is in response to, but by no means a dig on the blog post written by Zard-x on Medium called ‘&lt;a href="https://javascript.plainenglish.io/i-wrote-these-10-single-lines-of-javascript-code-the-team-lead-praised-the-code-for-being-elegant-6a9c471a9613"&gt;I Wrote These 10+ Single Lines of JavaScript Code; the Team Lead Praised the Code for Being Elegant&lt;/a&gt;’.&lt;/p&gt;

&lt;p&gt;One-liners are really cool challenges and very fun to make. I used to spend a lot of my time on &lt;a href="http://codewars.com"&gt;codewars.com&lt;/a&gt; and &lt;a href="http://codingame.com"&gt;codingame.com&lt;/a&gt;, which are two websites that facilitates little coding challenges which challenge your ability to write short code, or write code fast.&lt;/p&gt;

&lt;p&gt;While code golf can serve as a fun engineering exercise, their lessons should rarely be used in the real world—and when they are—by no means should engineering managers be promoting developers to use this type of code in a codebase. When you’re in a collaborative environment, writing &lt;em&gt;good code&lt;/em&gt; should be a priority. That means code that is readable, maintainable, and runs reliably.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Code golf is a type of recreational computer programming competition in which participants strive to achieve the shortest possible source code that solves a certain problem.&lt;/p&gt;

&lt;p&gt;— Wikipedia, Code golf&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Array Deduplication
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Original Source Code
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;I’m not necessarily opposed to the common trick of removing duplicates from arrays by instantiating, and spreading a set, but due to the amount of times you’re indexing the array, it can be quite poor performance-wise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rewritten Source Code
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;On an array of 1,000,000 randomly generated numbers between 1 - 100, the original source code only performs at 12% of the speed that the rewritten version does. Since we use a map in the second function, we only have to iterate once over the array, and made it so that checking to see if we’ve already seen an element takes O(1) time.&lt;/p&gt;

&lt;p&gt;The code eliminates “tricks,” and instead implores a well-known concept like hash maps, which may reduce the occurrence of inquiries by other developers on your code. The code also has well-named variables, with the function name &lt;code&gt;removeDuplicateStrings&lt;/code&gt; defining the action that the function is doing, rather than an ambiguous name like &lt;code&gt;uniqueArr&lt;/code&gt;, it also does not use excessively abbreviated words, making the code easier for non-native English speakers to understand.&lt;/p&gt;

&lt;p&gt;Notice that the function is not documented. Excessive documentation can sometimes be a bad thing. If your variable names are well named, you may find your comments redundant, and that’s a good thing! If that happens, happily drop ‘em, and ship it!&lt;/p&gt;

&lt;p&gt;If we’re absolutely dead-set on using spread operators and sets, or we want to remove duplicates of values that are not exclusively strings without writing bloated functionality, all we need to do is document our code, and ensure our variables are well-named.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Get Parameters from a URL and Convert Them to Object
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Original Source Code
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;If there was a bug with this code, how excited would you be when you figured out this is the function you’d be working on? Odds are, not so much. Not to mention that re-building JSON objects with strings is something you want to avoid as much as possible, in this case that’d be quite easy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rewritten Source Code
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In the rewritten code for this function, we use the convenient native JavaScript URL API which automatically invokes an instance of the URLSearchParams object for us, then we simply use the &lt;code&gt;Object#fromEntries&lt;/code&gt; function on the resulting object! This prevents us from having to manually construct a JSON object by mingling and mangling strings.&lt;/p&gt;

&lt;p&gt;Notice that we’ve provided a comment for this function! Despite the fact that I’ve included a specification for which &lt;em&gt;type&lt;/em&gt; of parameters the function handles in the name of the function &lt;code&gt;getURLParameters&lt;/code&gt;, it’s not necessarily clear what format the resulting object would be in.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check Whether the Object is Empty
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Original Source Code
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Using ownKeys or Object.keys are both not very performant when checking if an object is empty.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rewritten Source Code
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;This rewritten code is an old school method, and man has it passed the test of time! The re-written source code performs 20 times faster than the original method, and that’s only with two properties on an object. The performance boost comes from the fact that you no longer need to create arrays, copy over values, and index keys. You see one key and you’ve escaped the function!&lt;/p&gt;

&lt;p&gt;It might not immediately be obvious why we’re iterating over the keys of the object, so we’ve left a short comment in there to ensure that any other developers know what’s going on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reverse the String
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Original Source Code
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;In this case, we’re not off to a bad start! We could use a for-loop to get an 80% performance boost on short strings, and we might consider implementing that method if we’re reversing some chunky strings often, but in the case of the odd reversal on lightweight strings, we might just be better off with this functionality.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rewritten Source Code
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Splitting the chained methods across multiple lines is a personal preference of mine, as it allows me to scan the function paths rather than jumping through them, however if someone asked me to change it I wouldn’t make a fuss.&lt;/p&gt;

&lt;p&gt;For fun, here is a more performant version.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  Generate Random Hex
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Original Source Code
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Once again, not off to a bad start! I particularly like that we’re directly referencing the maximum hexadecimal value supported for non-alpha colours! Some minor cleanup, and we’ll be in business!&lt;/p&gt;

&lt;h3&gt;
  
  
  Rewritten Source Code
&lt;/h3&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;The only major change I made in this example is that I changed &lt;code&gt;padEnd&lt;/code&gt; to &lt;code&gt;padStart&lt;/code&gt;. The way this function works is by generating a random 24-bit integer, and then representing it in its hexadecimal format.&lt;/p&gt;

&lt;p&gt;The minimum value we can get is 0 &lt;code&gt;0x000000&lt;/code&gt;, with the highest value being 16,777,215 &lt;code&gt;0xFFFFFF&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s say our &lt;code&gt;randomValue&lt;/code&gt; ended up being 255. Our &lt;code&gt;hexValue&lt;/code&gt; would then be &lt;code&gt;ff&lt;/code&gt;, and if we padded the end, our return value would be &lt;code&gt;#ff0000&lt;/code&gt;, versus if we padded the beginning it would be &lt;code&gt;#0000ff&lt;/code&gt;. If we then translate both these values back into integers, &lt;code&gt;#ff0000&lt;/code&gt; would actually represent the value 16,711,680, while &lt;code&gt;#0000ff&lt;/code&gt; would represent 255. This means that if we pad the end rather than the beginning, we’d effectively get the wrong value.&lt;/p&gt;

&lt;p&gt;In addition to this fact, with the first method, by padding the end rather than the beginning, &lt;code&gt;#0000ff&lt;/code&gt; would actually be impossible to generate. Meaning there are plenty of missing values that could not possibly be generated. Specifically, every value between &lt;code&gt;0x000001&lt;/code&gt; and &lt;code&gt;0x100000&lt;/code&gt;. That is 1,048,576 missing values from our function, but would also tip the odds in favour of all values that end with a 0, some more than others. For example, #100000 would be the result from &lt;code&gt;0x1&lt;/code&gt;, &lt;code&gt;0x10&lt;/code&gt;, &lt;code&gt;0x100&lt;/code&gt;, &lt;code&gt;0x1000&lt;/code&gt;, &lt;code&gt;0x10000&lt;/code&gt;, and of course &lt;code&gt;0x100000&lt;/code&gt;. &lt;/p&gt;

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

&lt;p&gt;That took longer than expected, so I’ll be posting a second part to this blog post at a later date.  I had a ton of fun refactoring this code. Would you have done something differently? Leave a comment on &lt;a href="https://medium.com/@ridafkih/i-re-wrote-these-10-single-lines-of-javascript-code-the-team-lead-praised-the-code-for-being-668ade4fea71"&gt;Medium&lt;/a&gt; or &lt;a href="https://dev.to/rida/i-re-wrote-these-10-single-lines-of-javascript-code-the-team-lead-praised-the-code-for-being-elegant-ec"&gt;dev.to&lt;/a&gt;! I think this is a great learning opportunity for myself and others that might be in their code-golfing phase.&lt;/p&gt;

&lt;p&gt;Want to read more of my stuff? You can check out my &lt;a href="https://rida.dev/"&gt;personal website&lt;/a&gt;. If you’d like to start a dialogue, feel free to &lt;a href="https://twitter.com/ridafkih/"&gt;tweet at me&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>beginners</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Happy 20th birthday Jira! You suck so bad.</title>
      <dc:creator>Rida F'kih</dc:creator>
      <pubDate>Thu, 06 Oct 2022 06:22:31 +0000</pubDate>
      <link>https://forem.com/rida/happy-20th-birthday-jira-you-suck-so-bad-bno</link>
      <guid>https://forem.com/rida/happy-20th-birthday-jira-you-suck-so-bad-bno</guid>
      <description>&lt;h2&gt;
  
  
  What is Jira?
&lt;/h2&gt;

&lt;p&gt;For those lucky enough not to know, Jira is the biggest and by far most popular project management and issue tracking tool in and out of the software development space, developed by Atlassian, founded in 2002, and still one of Australia’s biggest international startups.&lt;/p&gt;

&lt;p&gt;Atlassian has created an insanely powerful, customizable tool which boasts robust integrations, every product management feature you could ask for, and yet it seems that it’s often not the case that it’s seen as a piece of software more than it’s seen as a way of life. People live by Jira, and they’ll die by Jira, and I think it just sucks.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does Jira suck?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The Workflow Detriment
&lt;/h3&gt;

&lt;p&gt;Jira—as the software that handles my issues in a daily basis—&lt;em&gt;should be&lt;/em&gt; the piece of software that I interact with the most on the daily aside from a team communication platform. ie. Discord, Slack, etc.&lt;/p&gt;

&lt;p&gt;However, as much as I want to use Jira in that way, it just can’t be well-integrated into my workflow. It is tucked away into my web-browser, with slow load times combined with convoluted &amp;amp; unintuitive nested navigation to get where I need to go. I &lt;em&gt;should not need&lt;/em&gt; to have a special, dedicated “Jira Hole” in my bookmarks to use it effectively.&lt;/p&gt;

&lt;h3&gt;
  
  
  Design Oversight Extravaganza
&lt;/h3&gt;

&lt;p&gt;Jira is riddled with what are quite frankly some of the worst design oversights I have seen in any piece of “enterprise ready” software in my life.&lt;/p&gt;

&lt;p&gt;Dare you accidentally hit the wrong key while crafting a lengthy, yet beautifully written text: it shall be sent to the nether never to see again, not a warning in sight. I often find myself in navigation loops, find myself making dead-clicks, or having to click buttons that are not buttons **to get where I need to go.&lt;/p&gt;

&lt;p&gt;User experience &amp;amp; user interface design is a very specialized and developed topic in 2022, I’d even consider it a science at this point, and Atlassian has managed to do it so poorly I simply cannot even fathom what is going on behind the scenes. I am a very download-happy person when it comes to trying out new software, and Jira’s user interface has to be the least intuitive, worst-design piece of garbage I have ever had the displeasure of laying my eyes on.&lt;/p&gt;

&lt;p&gt;For software with origins in software development, it baffles me that Jira doesn’t support dark-mode. This means every time I open it, I get flash-banged.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accessibility &amp;amp; Responsiveness
&lt;/h3&gt;

&lt;p&gt;Thankfully in 2022, 20-years after it was created, Jira has finally addressed the inability for non-sighted users to use their software, yet it’s still not responsiveness to varying screen sizes. Come on.&lt;/p&gt;

&lt;h3&gt;
  
  
  Context Switching
&lt;/h3&gt;

&lt;p&gt;The lack of a native application combined with the unintuitive UI, convoluted navigation, long loading times, content-layout shifts, inscrutable elements, etc., switching to Jira to edit, update, change or comment on a ticket requires a genuine need for context switching. Meaning it’s very common that you’ll lose momentum on tasks with higher cognitive load directly resulting from Jira.&lt;/p&gt;

&lt;p&gt;The cluttered UI alone makes me feel like I’m operating a Boeing 747 in the comfort of my ergonomic chair, all while only having to flip one or two switches and click a button.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;p&gt;Working with Jira is like using the controls of military tank to drive a Tuk-Tuk. Complicated yet slow. Fun fact, when you open lightly populated Jira board view, you download &lt;strong&gt;~24MB&lt;/strong&gt; of data cross &lt;strong&gt;~350 requests&lt;/strong&gt;, and every ticket you preview is an additional &lt;strong&gt;~20MB&lt;/strong&gt; across &lt;strong&gt;~90 requests&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That means if you open the Jira board view, and preview 10 tickets, that’s almost a &lt;strong&gt;quarter of a gigabyte&lt;/strong&gt; that you just downloaded across over &lt;strong&gt;1.25K requests&lt;/strong&gt;, much of it uncached and redundant.&lt;/p&gt;

&lt;p&gt;If we compare this to Notion with a highly-populated workspace, it initially downloads &lt;strong&gt;~13.5MB&lt;/strong&gt; over &lt;strong&gt;~120 requests&lt;/strong&gt;, with each previewed page downloading only about &lt;strong&gt;~0.2MB&lt;/strong&gt; over &lt;strong&gt;~20 requests.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Customization Galore
&lt;/h3&gt;

&lt;p&gt;One complaint many others have with Jira is just how customizable it is, management will go absolutely crazy with making Jira a completely new product, implement blocking processes and intense red-tape, and that’s why when someone complains about Jira the catch-all response is either “you’ve configured it wrong,” or “you’re using it wrong.”&lt;/p&gt;

&lt;p&gt;Despite that fact, my experience with Jira has been rather minimalistic, never have I been on a team with overcomplicated processes around Jira itself, nor did we go crazy with the customizations, yet it was still problematic and abrasive to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does Jira suck?
&lt;/h2&gt;

&lt;p&gt;Jira promotes bureaucracy-rich workflows that are built to convenience off-the-floor workers, managers, executives, etc., at the expense of those who are interfacing with it on a daily basis. As I outlined in the intro of this post, Jira is an insanely powerful tool, just not for the people who have their sleeves rolled up high. It generates amazing reports, fantastic predictions, fancy charts, great managerial tooling, makes imposing processes easier, has robust integrations, etc., and that’s why most managers I’ve spoken to really enjoy Jira, while on-the-floor engineers either range from fiery-eyed hate to toleration.&lt;/p&gt;

&lt;p&gt;I think Jira does a good job of making teams look much busier than they are, but in the end I see it more as a “talk more not do more” situation.&lt;/p&gt;

&lt;p&gt;A very common response I get when I ask developers why they use Jira is because they either don’t care, or haven’t found a suitable alternative.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Alternative
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FndRlQnp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fi.imgur.com%2FndRlQnp.png" alt="https://i.imgur.com/ndRlQnp.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Linear
&lt;/h3&gt;

&lt;p&gt;Linear is the perfect antithesis of Jira. Rather than a slow, loose, unintuitive ticket management experience, Linear provides a highly fast and intuitive UI with realtime changes wrapped in a beautifully crafted, opinionated platform built with software developers in mind. It cuts out the unnecessary bureaucracy and leaves engineers with a ticket management experience that integrates very well into their day-to-day thanks to its desktop application, theme customizability, command palette, offline functionality, and speed. It integrates into a plethora of software including Zendesk, Intercom, Figma, GitHub, GitLab, Zapier, and gives you full-access to built custom integrations using their GraphQL interface.&lt;/p&gt;

&lt;p&gt;Linear has been a pleasure to use on the teams I’ve worked on that have opted for it, as well as a no-brainer for collaborative personal projects. Although a potential downside to Linear is that it is built explicitly for engineers, thus may not be suitable for other teams such as marketing, design, etc.&lt;/p&gt;

&lt;h3&gt;
  
  
  Other Alternatives
&lt;/h3&gt;

&lt;p&gt;If you’ve given Linear a try, but it just doesn’t work for you, some viable alternatives include GitHub Issues, Asana, ClickUp, YouTrack, Trello, Pivotal Tracker, and monday.com.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real Quotes from Jira Users
&lt;/h2&gt;

&lt;p&gt;If my article wasn’t enough of an articulation of the common frustrations with Jira, a small directory of quotes exists on a website called &lt;a href="http://ifuckinghatejira.com" rel="noopener noreferrer"&gt;ifuckinghatejira.com&lt;/a&gt;, here are a few excerpts. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Jira is middle-management-ware, a term I made up for software that serves the needs of middle management, or, at least, the needs middle management thinks it has, which comes to the same thing as long as you’re selling to them. What are those needs? Metrics. Being able to make charts and graphs and summarized reports for the next-higher level of management, so they can say that their team is productive.&lt;/p&gt;

&lt;p&gt;Every time I open Confluence or Jira I want to claw my fucking eyes out. It’s slow, hard to navigate, and terrible.&lt;/p&gt;

&lt;p&gt;Tickets in JIRA are not the work itself, never was and never will be, it is a LARP of the work, but it gets taken for the central thing. This is an illusion. Fixing a bug without filing a JIRA ticket is in itself progress. Moving a JIRA card without any other change is not. Yet the second is what’s visible and therefor what’s rewarded.&lt;/p&gt;

&lt;p&gt;I hate Jira. You need to watch a few hours of tutorials to be able to understand this piece of crap.&lt;/p&gt;

&lt;p&gt;The momentum of its user base plus incoherent analyst opinions only serve to discourage moving to something actually useful/modern/user centered/updated/etc.&lt;/p&gt;

&lt;p&gt;It is bad, really bad, like come on your page takes 5 seconds to load on every click with a 100Mb internet and loads of ram, seriously? I don’t want to talk about the super unintuitive interface. It is basically cancer. And you can’t use something else if your organisation decides to use it.&lt;/p&gt;

&lt;p&gt;it’s not meant for devs but for project managers - and project managers like to make the devs do their job in that horrific tool. Imo there should be a dev view that is dead simple, and a PM view with all the complicated bells and whistles.&lt;/p&gt;

&lt;p&gt;Even stripped down, it’s still too complicated. The wealth of features tend to lead to practices that can be bad for overall productivity even if they look useful on the surface.&lt;/p&gt;

&lt;p&gt;JIRA is powerful. BUT… the UX is bloody awful. As in makes me want to stab myself in the eyes daily. The number of times I’ve lost content due to terrible and buggy UI is growing numerous.&lt;/p&gt;

&lt;p&gt;JIRA. Fucking JIRA. Everybody just fucking hates it. It tops the list of shit pieces of software by a fair margin, followed by JIRA in second place and JIRA in 3rd.&lt;/p&gt;

&lt;p&gt;Jira presents an excellent exercise on how not to do UX. The interface is slow, cluttered and unresponsive. Want to figure out how to do anything? God help you, because the design certainly won’t.&lt;/p&gt;

&lt;p&gt;I avoid doing any kind of work with JIRA besides what’s absolutely necessary - and even then I pull my hair out in frustration.&lt;/p&gt;

&lt;p&gt;Jira is so mind bogglingly awful, clunky and slow, I would rather peel off my skin and bathe my weeping raw flesh in a bath of vinegar than have to use Jira for one more day!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="http://ifuckinghatejira.com" rel="noopener noreferrer"&gt;ifuckinghatejira.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>programming</category>
      <category>webdev</category>
      <category>devops</category>
    </item>
    <item>
      <title>The Power of Pissing Code, and its Place in Answering the Age-Old Question: “TypeScript, or JavaScript?”</title>
      <dc:creator>Rida F'kih</dc:creator>
      <pubDate>Mon, 20 Jun 2022 07:27:15 +0000</pubDate>
      <link>https://forem.com/rida/the-power-of-pissing-code-and-its-place-in-answering-the-age-old-question-typescript-or-javascript-2pbn</link>
      <guid>https://forem.com/rida/the-power-of-pissing-code-and-its-place-in-answering-the-age-old-question-typescript-or-javascript-2pbn</guid>
      <description>&lt;p&gt;New developers often ask whether they should use JavaScript or TypeScript, and while the answer will almost always be TypeScript, writing JavaScript has its place.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;TypeError: Cannot read property 'typeScriptOrJavaScript' of undefined&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;A classic error. Some might say a rite of passage even.&lt;/p&gt;

&lt;p&gt;Every developer has experienced it, and most developers are now equipped with both the experience and tools to avoid silly little errors like it. One of those tools? &lt;strong&gt;TypeScript.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For the unwitting beginner, this error stems from accidentally (or purposefully, if you’re a masochist) assuming an &lt;code&gt;undefined&lt;/code&gt; variable is an object, and trying to access a property of that undefined value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;undefinedVariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;undefinedVariable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nonExistentProperty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// TypeError: Cannot read property 'nonExistentProperty' of undefined&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  TypeScript to the Rescue
&lt;/h2&gt;

&lt;p&gt;With TypeScript, small issues like this can be caught during development, meaning they’ll never see the light of day in runtime. With no changes to the above snippet, TypeScript would have warned the developer that the code is invalid outlining exactly what needs to be changed, in this case: ensuring &lt;code&gt;undefinedVariable&lt;/code&gt; had a property called &lt;code&gt;nonExistentProperty&lt;/code&gt;, or removing the invalid property access attempt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;ActuallyDefinedVariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;nonExistentProperty&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;undefinedVariable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ActuallyDefinedVariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;nonExistentProperty&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;I'm defined!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;undefinedVariable&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nonExistentProperty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ✅ TypeScript Approves&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Forbidden Keyword
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Any&lt;/em&gt; language has its own set of bad practices, and TypeScript isn’t immune. I’ve seen both beginners and experienced developers alike resort to the keyword &lt;code&gt;any&lt;/code&gt;, which is a catch-all type in TypeScript. It allows you to essentially bring JavaScript to TypeScript by removing static typing for a variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;numberVariable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;numberVariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ❌ TypeScript Error&lt;/span&gt;

&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;anyVariable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;any&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;anyVariable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// ✅ TypeScript Approves (but we don't)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When complex typing becomes an issue, some developers simply resort to &lt;code&gt;any&lt;/code&gt;, but this defeats the purpose of TypeScript by removing the nice, strict typing that TypeScript provides. I personally use ESLint to disallow &lt;code&gt;any&lt;/code&gt; from appearing anywhere in codebases I’m collaborating on, and I recommend you do to.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript or JavaScript?
&lt;/h2&gt;

&lt;p&gt;This is a question that every beginner wants to know. TypeScript can seem like a hurdle to newbies, and she’s definitely more intimidating than her older sister: JavaScript, but once you get over the relatively small learning curve, the benefits pay off immensely.&lt;/p&gt;

&lt;p&gt;I’m hard-struck for a reason to tell anyone to go with JavaScript on any project with even an ounce of complexity. TypeScript’s static typing improves the developer experience, and eliminates time lost debugging. Not to mention the improvement to ease-of-collaboration through the enforcement of better practices, making code more skim-friendly &amp;amp; clear.&lt;/p&gt;

&lt;p&gt;However, I would be lying if I said that writing raw JavaScript didn’t have its place.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Power of Pissing Code
&lt;/h2&gt;

&lt;p&gt;Pissing code is a rather inelegant name for a style of coding I and other developers often find themselves doing. Similar to shaking your bike lock every time you pass it to make sure its not magically at risk of being stolen, sometimes we developers like to double check to make sure the language we’re working with is going to function the way we expect it to.&lt;/p&gt;

&lt;p&gt;Perhaps you want to quickly plan out a function before introducing it into a library, test a build-in method before using it in a codebase, maybe you’re just trying something new, or you simply need to write a one-off script you’re never going to touch again.&lt;/p&gt;

&lt;p&gt;If there is no permanence, complexity, or collaboration intended for the code you’re writing, piss it into a one-off JavaScript file or a Quokka session and call it a day. Since the codebase isn’t living on, the benefits become less tangible and rewarding. If the code is short-lived, or you’re just testing something out, just &lt;em&gt;piss it into a file&lt;/em&gt;.&lt;/p&gt;

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

&lt;p&gt;If the code is hitting version control, you’re collaborating with others, the code is complex, or its going to be used multiple times, its best to use TypeScript and reap the rewards, otherwise don’t feel guilty for pissing the code into a file or repl you’re never going to touch again! &lt;/p&gt;

&lt;p&gt;If you liked this post, check me out &lt;a href="https://twitter.com/ridafkih"&gt;on Twitter&lt;/a&gt; to be kept up to date on future posts! ❤️&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Maintaining TypeScript Superpowers When Types Are Out of Reach</title>
      <dc:creator>Rida F'kih</dc:creator>
      <pubDate>Wed, 13 Apr 2022 15:51:02 +0000</pubDate>
      <link>https://forem.com/rida/maintaining-typescript-superpowers-when-types-are-out-of-reach-4j8</link>
      <guid>https://forem.com/rida/maintaining-typescript-superpowers-when-types-are-out-of-reach-4j8</guid>
      <description>&lt;p&gt;It’s all too common. You’re coding away in TypeScript, utilizing an external library, and you’re digging around the code for that type you just can’t find and… bam! The type you’ve been searching for is not exported!&lt;/p&gt;

&lt;p&gt;You could either re-create the types locally, fork the repository and export the types, or work with what you’ve got! Today, I’m going to show you how to do the latter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inferred Return Types
&lt;/h2&gt;

&lt;p&gt;Let's say we're utilizing a function from an external library which returns an object that lacks explicitly defined type information as follows.&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;const&lt;/span&gt; &lt;span class="nx"&gt;divideNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dividend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;divisor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;quotient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;dividend&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;divisor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dividend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;divisor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quotient&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * This function serves as an example of a function which returns
 * an inferred type, meaning the return type is not explicitly
 * defined by the developer.
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When using this function, TypeScript will automatically infer the type as follows.&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="p"&gt;{&lt;/span&gt;
     &lt;span class="nl"&gt;dividend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="nl"&gt;divisor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="nl"&gt;quotient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * This is what the inferred return type of the previous function 
 * looks like.
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is fine for basic usage, but if we want to do something with the type, it may become difficult.&lt;/p&gt;

&lt;p&gt;If you're unfamiliar with the &lt;code&gt;ReturnType&lt;/code&gt;, your first instinct might be to recreate the type yourself as follows, but doing so means we’ll be deviating from a single source of truth.&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;DivisionObject&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;dividend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;divisor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;quotient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * This is a bad example of a solution to define a type for the
 * return type of the previous function.
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to &lt;code&gt;ReturnType&lt;/code&gt;, which takes a generic—that being a function type signature—returns its return type, even if its not explicitly named.&lt;/p&gt;

&lt;p&gt;We can either use it directly, or assign it to our own named type.&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="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;divideNumber&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@fake-library/math&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;DivisionObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;divideNumber&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Here we are using the ReturnType generic to define a type as
 * the inferred return type of the divideNumber function.
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we are able to use this as a property in another local function.&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;const&lt;/span&gt; &lt;span class="nx"&gt;verifyDivision&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;dividend&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;divisor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;quotient&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;DivisionObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;divisor&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;dividend&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;quotient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Here we're taking the type we defined and defining it as the
 * first property of the verifyDivision function.
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎉 Awesome! This is so much better!&lt;/p&gt;

&lt;h2&gt;
  
  
  Nested, Unnamed Types
&lt;/h2&gt;

&lt;p&gt;Sometimes we do have an exported type, but we just want one property of that type. Again, if you're unfamiliar with TypeScript your first instinct might be to simply redeclare the type. However, we can actually access that by simply using our handy square brackets.&lt;/p&gt;

&lt;p&gt;Let’s say we have the following interface.&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="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;HumanBeing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;firstName&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="nl"&gt;lastName&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="nl"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;country&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="nl"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;english&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;french&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;spanish&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;timeZone&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="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;Now let's say we want to create a function that checks if a human being’s language is English.&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;type&lt;/span&gt; &lt;span class="nx"&gt;HumanBeingLanguage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;HumanBeing&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;locale&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;language&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isLanguageEnglish&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HumanBeingLanguage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;language&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;english&lt;/span&gt;&lt;span class="dl"&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;✨ Easy peasy, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Variable Unnamed Object Type Signatures
&lt;/h2&gt;

&lt;p&gt;This one is a little more obscure, but something I ran into while creating a custom Notion renderer for the &lt;a href="https://rida.dev/"&gt;latest version of my personal portfolio&lt;/a&gt;. If we have an array where we have multiple possible type signatures of objects, and the possible object types in question are not named, things can get yucky very quickly.&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;type&lt;/span&gt; &lt;span class="nx"&gt;CustomHTMLElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;src&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="nl"&gt;alt&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="p"&gt;};&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="na"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;video&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nl"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;src&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="nl"&gt;type&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="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * As you can see, the above type can have multiple different
 * object type signatures, despite being under the same namespace.
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the attributes are inconsistent.&lt;/p&gt;

&lt;p&gt;Let’s define an array of objects which utilizes our &lt;code&gt;CustomHTMLElement&lt;/code&gt; type.&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;const&lt;/span&gt; &lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CustomHTMLElement&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="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/image.png&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Example image&lt;/span&gt;&lt;span class="dl"&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;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;video&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://example.com/video.mp4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;video/mp4&lt;/span&gt;&lt;span class="dl"&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;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's say we want to get a list of all the &lt;code&gt;img&lt;/code&gt; elements, our first instinct might be to use the raw &lt;code&gt;Array#filter&lt;/code&gt; method.&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;const&lt;/span&gt; &lt;span class="nx"&gt;getAllImgElements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;getAllImgElements&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/** Property 'alt' does not exist on type
                            '{ src: string; alt: string; }
                           | { src: string; type: string; }'.
                                                    */&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Unfortunately, using filter with this type, we get an 
 * error since TypeScript cannot make an inference from this
 * method.
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, the problem with this is &lt;code&gt;Array#filter&lt;/code&gt; does not immediately respect type inferences. TypeScript still has no idea that any of the other object types have been eliminated as a possibility.&lt;/p&gt;

&lt;p&gt;In this case, the inferred type of the return type of the &lt;code&gt;getAllImgElements&lt;/code&gt; function is &lt;code&gt;() =&amp;gt; CustomHTMLElement[]&lt;/code&gt;, so we still have the exact same original degree of ambiguity.&lt;/p&gt;

&lt;p&gt;Thankfully, we can leverage the &lt;code&gt;Extract&lt;/code&gt; generic type available in TypeScript 2.8+ combined with a TypeScript predicate to tell TypeScript what to expect after we’ve filtered.&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;type&lt;/span&gt; &lt;span class="nx"&gt;CustomHTMLElementTag&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Extract&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CustomHTMLElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getAllImgElements&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;CustomHTMLElementTag&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;getAllImgElements&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="cm"&gt;/** TypeScript is happy! 🎉 */&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Here, we're using a type guard, a type predicate, and the
 * TypeScript Extract generic to tell TypeScript which type
 * is making it out the other end. 
 */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;TypeScript is an absolute beast, but because of the nature of its subset language: JavaScript, things can get a little funky sometimes. Luckily, we all love a challenge. &lt;/p&gt;

&lt;p&gt;You can read more content like this on my &lt;a href="https://rida.dev/"&gt;personal blog!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you liked this post, check me out on &lt;a href="https://twitter.com/ridafkih"&gt;Twitter&lt;/a&gt; to be kept up to date on future posts! ❤️&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Not Expose the Personal Data of 19,577 Canadians</title>
      <dc:creator>Rida F'kih</dc:creator>
      <pubDate>Tue, 12 Apr 2022 17:34:46 +0000</pubDate>
      <link>https://forem.com/rida/how-to-not-expose-the-personal-data-of-19577-canadians-41gf</link>
      <guid>https://forem.com/rida/how-to-not-expose-the-personal-data-of-19577-canadians-41gf</guid>
      <description>&lt;p&gt;A few months ago I spoke with &lt;a href="https://www.cbc.ca/news/canada/calgary/portpass-app-proof-of-vaccination-unsecured-data-update-1.6229034"&gt;CBC news&lt;/a&gt; to give an overview of a plethora of security flaws I found in PORTpass, an attempt to bring digital proof-of-vaccine to Canadians. While that article was meant for a broader audience, today I will outline their security flaws from a technical perspective for all the developers out there.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Background
&lt;/h2&gt;

&lt;p&gt;My name is Rida F’kih, I am a 21 year old Canadian software developer &amp;amp; reverse engineer at &lt;a href="https://maxrewards.com/"&gt;MaxRewards.&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;My development journey started at 15, from reverse-engineering to scammer-vigilantism then teaching coding to kids to web &amp;amp; mobile development.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is PORTpass?
&lt;/h2&gt;

&lt;p&gt;PORTpass was a private attempt by a local Calgarian entrepreneur to bring vaccine passports to Canadians.&lt;/p&gt;

&lt;h2&gt;
  
  
  What went wrong?
&lt;/h2&gt;

&lt;p&gt;First, we must familiarize ourselves with Murphy’s Law.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Murphy’s law is an adage or epigram that is typically stated as: “Anything that can go wrong will go wrong."&lt;/p&gt;

&lt;p&gt;— Wikipedia, Murphy’s Law&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I’d argue that PORTpass was the epitome of Murphy's Law from the perspective of the user, and criminal negligence on behalf of its creator due to their inaction after these issues were brought to light.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Data at Risk
&lt;/h2&gt;

&lt;p&gt;The data at risk wasn’t light stuff.&lt;/p&gt;

&lt;p&gt;PORTpass had the business requirement of collecting loads of personal data, collecting photos of government IDs, health card numbers, verification selfies, full names, dates of births, blood types, and more.&lt;/p&gt;

&lt;p&gt;The data on PORTpass would be enough to be at risk of social engineering, or even have financial accounts opened in your name.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Vulnerability
&lt;/h2&gt;

&lt;p&gt;The primary vulnerability is called “Improper Access Control,” which is described by the &lt;a href="https://cwe.mitre.org/"&gt;Common Weakness Enumeration&lt;/a&gt;—a community-developed list of software &amp;amp; hardware weakness types—as “software [that] does not restrict or incorrectly restricts access to a resource from what should be an unauthorized actor.”&lt;/p&gt;

&lt;p&gt;Some pseudo-code to describe a properly managed &amp;amp; created endpoint may appear as follows.&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="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;loggedInUser&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/utils/authentication&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;getUserInfo&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@/utils/users&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/user/:userId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authenticatedUserId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;loggedInUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;authenticatedUserId&lt;/span&gt;  &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;authenticatedUserId&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unauthorized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;else&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getUserInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&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;Here, we would send a GET request to &lt;code&gt;/user/[userId]&lt;/code&gt;, and receive back user data only if we are authenticated into the same user we are requesting.&lt;/p&gt;

&lt;p&gt;A poorly architected endpoint may be coded as follows.&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/user/:userId&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;params&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="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;loggedInUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unauthorized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="k"&gt;else&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;getUserInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&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;Here, we’re not checking &lt;em&gt;which&lt;/em&gt; user is authenticated, only &lt;em&gt;if&lt;/em&gt; the user is authenticated. Thus, an authenticated user could request ANY resource, even if it doesn’t belong to them. For public posts this is appropriate, for private information like government-issued photo IDs, health care cards, and confirmation portraits, this is a massive flaw.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Catalysts
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Sequential User IDs
&lt;/h3&gt;

&lt;p&gt;PORTpass users requested their resource by using sequential user IDs. This means that if you were the first user to sign up for their service, your ID would be &lt;code&gt;1&lt;/code&gt;, if you were the second, it would be &lt;code&gt;2&lt;/code&gt;, etc.&lt;/p&gt;

&lt;p&gt;Had they used a cryptographic solution—such as UUIDv4—scraping user information would have been significantly more difficult, however in this case formulating a script to collect all registered user data would be trivial, and would look something like...&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;/**
 * Gets the user profile from the PortPass API.
 * @param {number|string} userId The user ID of the target user.
 * @returns {Promise&amp;lt;any&amp;gt;} A user object.
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getUserProfile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;axios&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;USER_PROFILE_URI&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({}));&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;profile&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;19577&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;getUserProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;saveUserData&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  No SSL
&lt;/h3&gt;

&lt;p&gt;Since PORTpass didn't have SSL certificates, packets that your device sends or receives could be altered or intercepted. With websites, its easy to identify a secure website by finding the little "lock icon" next to the URL, however with applications its a little more ambiguous.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exposing Developer Tooling
&lt;/h3&gt;

&lt;p&gt;PORTpass' backend was created using Django, which has an interface which allows you to test endpoints from your browser without having to use REST tooling like Postman, or Insomnia.&lt;/p&gt;

&lt;p&gt;Optimally, PORTpass would have secured their endpoints so knowing their paths would provide no additional capabilities, however, they didn't, and thus traversing their entire backend was trivial.&lt;/p&gt;




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

&lt;p&gt;We all want to get our projects out there, but if we don't have the experience necessary to accommodate for basic security considerations, getting your code reviewed or learning the basics of security should be considered good options. &lt;/p&gt;

&lt;p&gt;In this case, a non-technical founder wanted to hastily get a product out there, so they hired a developer from overseas without the ability to evaluate the quality of code. &lt;/p&gt;

&lt;p&gt;I'm very thankful to the CBC for their help in finally being able to hold the creator of PORTpass accountable, an address these concerns rather than simply ignoring them or claiming to be defamed.&lt;/p&gt;

&lt;p&gt;You can read this article on &lt;a href="https://medium.com/@ridafkih/how-to-not-expose-the-personal-data-of-19-577-canadians-985669c9c1d9"&gt;Medium&lt;/a&gt; or my &lt;a href="https://www.rida.dev/blog/how-to-not-expose-the-personal-data-of-19-577-canadians-570451a57fd044afb694d5b3fb6fbdd0"&gt;personal website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you enjoyed this content please consider following me on &lt;a href="https://twitter.com/ridafkih/"&gt;Twitter&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>security</category>
      <category>beginners</category>
      <category>codenewbie</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
