{"id":456,"date":"2017-08-02T00:24:48","date_gmt":"2017-08-01T21:24:48","guid":{"rendered":"https:\/\/iamakulov.com\/notes\/?p=456"},"modified":"2017-08-18T17:37:51","modified_gmt":"2017-08-18T14:37:51","slug":"npm-malicious-packages","status":"publish","type":"post","link":"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/","title":{"rendered":"Malicious packages in npm. Here\u2019s what to do"},"content":{"rendered":"<p>Here\u2019s all the information I\u2019ve found.<\/p>\n<div class=\"bordered-highlight bordered-highlight_large\">\n\ud83d\udcbb\u2620\ufe0f\ud83c\udff4\n<\/div>\n<h1 id=\"what-happened\">What happened?<a href=\"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/#what-happened\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h1>\n<p>People found malicious packages in npm that work like real ones, are named similarly real ones, but collect and send your process environment to a third-party server when you install them: <\/p>\n<blockquote class=\"twitter-tweet\" data-lang=\"en\">\n<p lang=\"en\" dir=\"ltr\"><a href=\"https:\/\/twitter.com\/kentcdodds\">@kentcdodds<\/a> Hi Kent, it looks like this npm package is stealing env variables on install, using your cross-env package as bait: <a href=\"https:\/\/t.co\/REsRG8Exsx\">pic.twitter.com\/REsRG8Exsx<\/a><\/p>\n<p>&mdash; Oscar Bolmsten (@o_cee) <a href=\"https:\/\/twitter.com\/o_cee\/status\/892306836199800836\">August 1, 2017<\/a><\/p><\/blockquote>\n<p><script async src=\"\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"><\/script><\/p>\n<p>This is dangerous because, on CI servers, the environment usually includes different secret tokens.<\/p>\n<h1 id=\"what-to-do-if-im-a-user\">What to do if I\u2019m a user?<a href=\"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/#what-to-do-if-im-a-user\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h1>\n<p><mark>Regenerate the secret tokens<\/mark> if you installed any package from these as a dependency:<\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_no annotated-element_mobile-direction_content-then-annotation'>\n<div class='annotated-element__annotation'>\n<p><a href='http:\/\/imgur.com\/06bK0hj'>A screenshot<\/a> of the cached page with packages<\/p>\n<p>npm has also <a href='http:\/\/blog.npmjs.org\/post\/163723642530\/crossenv-malware-on-the-npm-registry'>confirmed this list<\/a><\/p>\n<\/div>\n<div class='annotated-element__content'>\n<pre><code class=\"text\">babelcli - v1.0.1 - Babel CLI for Nodejs\r\ncrossenv - v6.1.1 - Run scripts that set and use environment variables across platforms\r\ncross-env.js - v5.0.1\r\nd3.js - v1.0.1 - d3.js for Nodejs\r\nfabric-js - v1.7.18 - Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.\r\nffmepg - v0.0.1 - FFmpeg for Nodejs\r\ngruntcli - v1.0.1 - Grunt CLI for Nodejs\r\nhttp-proxy.js - v0.11.3 - Node.js proxy tools\r\njquery.js - v3.2.2-pre - jquery.js for Nodejs\r\nmariadb - v2.13.0 - A node.js driver for mysql. It is written in JavaScript, does not require compiling, and is 100% MIT licensed.\r\nmongose - v4.11.3 - Mongoose MongoDB ODM\r\nmssql.js - v4.0.5 - Microsoft SQL Server client for Node.js.\r\nmssql-node - v4.0.5 - Microsoft SQL Server client for Node.js.\r\nmysqljs - v2.13.0 - A node.js driver for mysql. It is written in JavaScript, does not require compiling, and is 100% MIT licensed.\r\nnodecaffe - v0.0.1 - caffe for Nodejs\r\nnodefabric - v1.7.18 - Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.\r\nnode-fabric - v1.7.18 - Object model for HTML5 canvas, and SVG-to-canvas parser. Backed by jsdom and node-canvas.\r\nnodeffmpeg - v0.0.1 - FFmpeg for Nodejs\r\nnodemailer-js - v4.0.1 - Easy as cake e-mail sending from your Node.js applications\r\nnodemailer.js - v4.0.1 - Easy as cake e-mail sending from your Node.js applications\r\nnodemssql - v4.0.5 - Microsoft SQL Server client for Node.js.\r\nnode-opencv - v1.0.1 - OpenCV for Nodejs\r\nnode-opensl - v1.0.1 - OpenSSL for Nodejs\r\nnode-openssl - v1.0.1 - OpenSSL for Nodejs\r\nnoderequest - v2.81.0 - Simplified HTTP request client.\r\nnodesass - v4.5.3 - Wrapper around libsass\r\nnodesqlite - v2.8.1 - SQLite client for Node.js applications with SQL-based migrations API\r\nnode-sqlite - v2.8.1 - SQLite client for Node.js applications with SQL-based migrations API\r\nnode-tkinter - v1.0.1 - Tkinter for Nodejs\r\nopencv.js - v1.0.1 - OpenCV for Nodejs\r\nopenssl.js - v1.0.1 - OpenSSL for Nodejs\r\nproxy.js - v0.11.3 - Node.js proxy tools\r\nshadowsock - v2.0.1 - A tunnel proxy that help you get through firewalls\r\nsmb - v1.5.1 - A Pure JavaScript SMB Server Implementation\r\nsqlite.js - v2.8.1 - SQLite client for Node.js applications with SQL-based migrations API\r\nsqliter - v2.8.1 - SQLite client for Node.js applications with SQL-based migrations API\r\nsqlserver - v4.0.5 - Microsoft SQL Server client for Node.js.\r\ntkinter - v1.0.1 - Tkinter for Nodejs\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>Here\u2019s also a one-liner that will list these packages if any of them was installed as a dependency:<\/p>\n<div class='annotated-element annotated-element_has-code_no annotated-element_keep-size_no annotated-element_mobile-direction_content-then-annotation'>\n<div class='annotated-element__annotation'>Twilio also listed one-liners <a href='https:\/\/www.twilio.com\/blog\/2017\/08\/find-projects-infected-by-malicious-npm-packages.html'>for checking the whole file system at once<\/a> (for bash and Powershell)<\/div>\n<div class='annotated-element__content'>\n<pre><code >npm ls | grep -E \"babelcli|crossenv|cross-env.js|d3.js|fabric-js|ffmepg|gruntcli|http-proxy.js|jquery.js|mariadb|mongose|mssql.js|mssql-node|mysqljs|nodecaffe|nodefabric|node-fabric|nodeffmpeg|nodemailer-js|nodemailer.js|nodemssql|node-opencv|node-opensl|node-openssl|noderequest|nodesass|nodesqlite|node-sqlite|node-tkinter|opencv.js|openssl.js|proxy.js|shadowsock|smb|sqlite.js|sqliter|sqlserver|tkinter\"\r\n<\/code><\/pre>\n<\/div>\n<\/div>\n<p>Always check the name of packages you\u2019re installing. You can look at the downloads number: if a package is popular but the downloads number is low, something is wrong.<\/p>\n<h1 id=\"what-to-do-if-im-a-library-developer\">What to do if I\u2019m a library developer?<a href=\"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/#what-to-do-if-im-a-library-developer\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h1>\n<p>I see two options:<\/p>\n<ul>\n<li>Use scopes (<code>@scope\/package-name<\/code>) for your packages. <del datetime=\"2017-08-09T13:22:09+00:00\">With scopes, it\u2019s harder to install a wrong package accidentally: a user would have to misspell both the scope name and the package name.<\/del> Unfortunately, it\u2019s not: it\u2019s enough to misspell just the scope name (e.g <code>@babel\/babel-cli<\/code> \u2192 <code>@bable\/babel-cli<\/code>). Scopes <em>might<\/em> help a bit because they can have a simple name that\u2019s harder to misspell, but it\u2019s still not a universal solution.<\/li>\n<li>Take the most common misspellings of your packages by yourself. Think of the most common misspellings and publish empty packages under these names. You can also warn users about the right name with <a href=\"https:\/\/docs.npmjs.com\/cli\/deprecate\"><code>npm deprecate<\/code><\/a>.<\/li>\n<\/ul>\n<h1 id=\"is-this-even-ok\">Is this even OK?<a href=\"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/#is-this-even-ok\" class=\"heading-link\" aria-label=\"Link to this section\" title=\"Link to this section\">#<\/a><\/h1>\n<p>This isn\u2019t surprising \u2013 npm doesn\u2019t have any protection against this yet. In fact, that\u2019s why there could be other malicious packages. Stay careful and check package names.<\/p>\n<p>npm is working on a solution though:<\/p>\n<blockquote class=\"twitter-tweet\" data-lang=\"en\">\n<p lang=\"en\" dir=\"ltr\">I&#39;m working on a thing that uses quality metrics and prompts users. It would probably catch just about everything folks have brought up<\/p>\n<p>&mdash; \u591a\u5206\u25ef\u3061\u3083\u3093\u3088\u306d\ud83d\udd75\ud83c\udffc\u200d\u2640\ufe0f (@maybekatz) <a href=\"https:\/\/twitter.com\/maybekatz\/status\/892501201551368192\">August 1, 2017<\/a><\/p><\/blockquote>\n<p><script async src=\"\/\/platform.twitter.com\/widgets.js\" charset=\"utf-8\"><\/script><\/p>\n<p>You can also participate in the Yarn\u2019s discussion <a href=\"https:\/\/github.com\/yarnpkg\/rfcs\/pull\/76\">about a white list for <code>preinstall<\/code>\/<code>postinstall<\/code> script packages<\/a>.<\/p>\n<hr \/>\n<p><!--p class=\"perf-consulting-snippet\">\n<mark>+1s in loading time of your app is \u22127% in conversion.<\/mark> I\u2019m launching consulting to help you make your React app or site load and run faster. Sign up as an early bird and get 10% discount: <a href=\"https:\/\/iamakulov.com\/perf-consulting\">iamakulov.com\/perf-consulting<\/a>\n<\/p-->\n","protected":false},"excerpt":{"rendered":"<p>Here\u2019s all the information I\u2019ve found. \ud83d\udcbb\u2620\ufe0f\ud83c\udff4 What happened?# People found malicious packages in npm that work like real ones, are named similarly real ones, but collect and send your process environment to a third-party server when you install them: @kentcdodds Hi Kent, it looks like this npm package is stealing env variables on install, &hellip; <a href=\"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Malicious packages in npm. Here\u2019s what to do&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[9],"class_list":["post-456","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-npm"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v28.6 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Malicious packages in npm. Here\u2019s what to do - Ivan Akulov\u2019s blog<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Malicious packages in npm. Here\u2019s what to do\" \/>\n<meta property=\"og:description\" content=\"If you\u2019re a user or a library author\" \/>\n<meta property=\"og:url\" content=\"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/\" \/>\n<meta property=\"og:site_name\" content=\"Ivan Akulov\u2019s blog\" \/>\n<meta property=\"article:publisher\" content=\"http:\/\/facebook.com\/iamakulov.page\" \/>\n<meta property=\"article:published_time\" content=\"2017-08-01T21:24:48+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2017-08-18T14:37:51+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/iamakulov.com\/notes\/wp-content\/uploads\/2017\/08\/ApplicationFrameHost_2017-08-02_00-20-43-1.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1426\" \/>\n\t<meta property=\"og:image:height\" content=\"713\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Ivan Akulov\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:title\" content=\"Malicious packages in npm. Here\u2019s what to do\" \/>\n<meta name=\"twitter:description\" content=\"If you\u2019re a user or a library author\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/i0.wp.com\/iamakulov.com\/notes\/wp-content\/uploads\/2017\/08\/ApplicationFrameHost_2017-08-02_00-20-43-1.png?fit=1426%2C713&ssl=1\" \/>\n<meta name=\"twitter:creator\" content=\"@iamakulov\" \/>\n<meta name=\"twitter:site\" content=\"@iamakulov\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Ivan Akulov\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/iamakulov.com\\\/notes\\\/npm-malicious-packages\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/iamakulov.com\\\/notes\\\/npm-malicious-packages\\\/\"},\"author\":{\"name\":\"Ivan Akulov\",\"@id\":\"https:\\\/\\\/iamakulov.com\\\/notes\\\/#\\\/schema\\\/person\\\/ebf7b61bf573e7be5fe438f50ebd9b81\"},\"headline\":\"Malicious packages in npm. Here\u2019s what to do\",\"datePublished\":\"2017-08-01T21:24:48+00:00\",\"dateModified\":\"2017-08-18T14:37:51+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/iamakulov.com\\\/notes\\\/npm-malicious-packages\\\/\"},\"wordCount\":877,\"commentCount\":4,\"keywords\":[\"npm\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/iamakulov.com\\\/notes\\\/npm-malicious-packages\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/iamakulov.com\\\/notes\\\/npm-malicious-packages\\\/\",\"url\":\"https:\\\/\\\/iamakulov.com\\\/notes\\\/npm-malicious-packages\\\/\",\"name\":\"Malicious packages in npm. Here\u2019s what to do - Ivan Akulov\u2019s blog\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/iamakulov.com\\\/notes\\\/#website\"},\"datePublished\":\"2017-08-01T21:24:48+00:00\",\"dateModified\":\"2017-08-18T14:37:51+00:00\",\"author\":{\"@id\":\"https:\\\/\\\/iamakulov.com\\\/notes\\\/#\\\/schema\\\/person\\\/ebf7b61bf573e7be5fe438f50ebd9b81\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/iamakulov.com\\\/notes\\\/npm-malicious-packages\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/iamakulov.com\\\/notes\\\/npm-malicious-packages\\\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/iamakulov.com\\\/notes\\\/npm-malicious-packages\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/iamakulov.com\\\/notes\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Malicious packages in npm. Here\u2019s what to do\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/iamakulov.com\\\/notes\\\/#website\",\"url\":\"https:\\\/\\\/iamakulov.com\\\/notes\\\/\",\"name\":\"Ivan Akulov\u2019s blog\",\"description\":\"Ivan Akulov writes about his front-end experience, React, webpack, and performance optimizations.\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/iamakulov.com\\\/notes\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/iamakulov.com\\\/notes\\\/#\\\/schema\\\/person\\\/ebf7b61bf573e7be5fe438f50ebd9b81\",\"name\":\"Ivan Akulov\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/aec4e9e944911b58f2c3d14b7f9e5412a217e5738359c3f52e824b4de2b2263c?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/aec4e9e944911b58f2c3d14b7f9e5412a217e5738359c3f52e824b4de2b2263c?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/aec4e9e944911b58f2c3d14b7f9e5412a217e5738359c3f52e824b4de2b2263c?s=96&d=mm&r=g\",\"caption\":\"Ivan Akulov\"},\"description\":\"I'm a software engineer specializing in web performance, JavaScript, and React. I\u2019m also a Google Developer Expert. I work at Framer.\",\"sameAs\":[\"http:\\\/\\\/iamakulov.com\",\"https:\\\/\\\/x.com\\\/iamakulov\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Malicious packages in npm. Here\u2019s what to do - Ivan Akulov\u2019s blog","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/","og_locale":"en_US","og_type":"article","og_title":"Malicious packages in npm. Here\u2019s what to do","og_description":"If you\u2019re a user or a library author","og_url":"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/","og_site_name":"Ivan Akulov\u2019s blog","article_publisher":"http:\/\/facebook.com\/iamakulov.page","article_published_time":"2017-08-01T21:24:48+00:00","article_modified_time":"2017-08-18T14:37:51+00:00","og_image":[{"width":1426,"height":713,"url":"https:\/\/iamakulov.com\/notes\/wp-content\/uploads\/2017\/08\/ApplicationFrameHost_2017-08-02_00-20-43-1.png","type":"image\/png"}],"author":"Ivan Akulov","twitter_card":"summary_large_image","twitter_title":"Malicious packages in npm. Here\u2019s what to do","twitter_description":"If you\u2019re a user or a library author","twitter_image":"https:\/\/i0.wp.com\/iamakulov.com\/notes\/wp-content\/uploads\/2017\/08\/ApplicationFrameHost_2017-08-02_00-20-43-1.png?fit=1426%2C713&ssl=1","twitter_creator":"@iamakulov","twitter_site":"@iamakulov","twitter_misc":{"Written by":"Ivan Akulov","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/#article","isPartOf":{"@id":"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/"},"author":{"name":"Ivan Akulov","@id":"https:\/\/iamakulov.com\/notes\/#\/schema\/person\/ebf7b61bf573e7be5fe438f50ebd9b81"},"headline":"Malicious packages in npm. Here\u2019s what to do","datePublished":"2017-08-01T21:24:48+00:00","dateModified":"2017-08-18T14:37:51+00:00","mainEntityOfPage":{"@id":"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/"},"wordCount":877,"commentCount":4,"keywords":["npm"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/","url":"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/","name":"Malicious packages in npm. Here\u2019s what to do - Ivan Akulov\u2019s blog","isPartOf":{"@id":"https:\/\/iamakulov.com\/notes\/#website"},"datePublished":"2017-08-01T21:24:48+00:00","dateModified":"2017-08-18T14:37:51+00:00","author":{"@id":"https:\/\/iamakulov.com\/notes\/#\/schema\/person\/ebf7b61bf573e7be5fe438f50ebd9b81"},"breadcrumb":{"@id":"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/iamakulov.com\/notes\/npm-malicious-packages\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/iamakulov.com\/notes\/"},{"@type":"ListItem","position":2,"name":"Malicious packages in npm. Here\u2019s what to do"}]},{"@type":"WebSite","@id":"https:\/\/iamakulov.com\/notes\/#website","url":"https:\/\/iamakulov.com\/notes\/","name":"Ivan Akulov\u2019s blog","description":"Ivan Akulov writes about his front-end experience, React, webpack, and performance optimizations.","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/iamakulov.com\/notes\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/iamakulov.com\/notes\/#\/schema\/person\/ebf7b61bf573e7be5fe438f50ebd9b81","name":"Ivan Akulov","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/aec4e9e944911b58f2c3d14b7f9e5412a217e5738359c3f52e824b4de2b2263c?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/aec4e9e944911b58f2c3d14b7f9e5412a217e5738359c3f52e824b4de2b2263c?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/aec4e9e944911b58f2c3d14b7f9e5412a217e5738359c3f52e824b4de2b2263c?s=96&d=mm&r=g","caption":"Ivan Akulov"},"description":"I'm a software engineer specializing in web performance, JavaScript, and React. I\u2019m also a Google Developer Expert. I work at Framer.","sameAs":["http:\/\/iamakulov.com","https:\/\/x.com\/iamakulov"]}]}},"jetpack_shortlink":"https:\/\/wp.me\/p8Hto2-7m","jetpack-related-posts":[{"id":1449,"url":"https:\/\/iamakulov.com\/notes\/case-study-analyzing-notions-app-performance\/","url_meta":{"origin":456,"position":0},"title":"Case study: Analyzing Notion\u2019s app performance","author":"Ivan Akulov","date":"June 9, 2020","format":"link","excerpt":"Here\u2019s another case study of mine! (The previous one was as far as two years ago, and it was about Walmart.) Notion is an advanced note-taking app written with React and Electron. In this case study, I look at how to improve its startup time by ~30% \u2013 by removing\u2026","rel":"","context":"Similar post","block_context":{"text":"Similar post","link":""},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":1453,"url":"https:\/\/iamakulov.com\/notes\/lessons-from-self-employment\/","url_meta":{"origin":456,"position":1},"title":"Lessons From Six Years of Self-Employment","author":"Ivan Akulov","date":"February 4, 2024","format":false,"excerpt":"From 2017 to 2023, I worked as a self-employed web performance consultant. I was lucky to do some cool stuff (work with Google and another company every American knows, help many hot startups, speak at worldwide conferences). In 2024, I turned that page and became an employee at Framer, to\u2026","rel":"","context":"Similar post","block_context":{"text":"Similar post","link":""},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/iamakulov.com\/notes\/wp-content\/uploads\/2024\/01\/Screenshot-2024-01-21-at-21.35.38.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/iamakulov.com\/notes\/wp-content\/uploads\/2024\/01\/Screenshot-2024-01-21-at-21.35.38.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/iamakulov.com\/notes\/wp-content\/uploads\/2024\/01\/Screenshot-2024-01-21-at-21.35.38.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/iamakulov.com\/notes\/wp-content\/uploads\/2024\/01\/Screenshot-2024-01-21-at-21.35.38.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/iamakulov.com\/notes\/wp-content\/uploads\/2024\/01\/Screenshot-2024-01-21-at-21.35.38.png?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/iamakulov.com\/notes\/wp-content\/uploads\/2024\/01\/Screenshot-2024-01-21-at-21.35.38.png?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":1065,"url":"https:\/\/iamakulov.com\/notes\/walmart\/","url_meta":{"origin":456,"position":2},"title":"Case study: analyzing the Walmart site performance","author":"Ivan Akulov","date":"April 27, 2018","format":false,"excerpt":"Walmart is one of the top USA e-commerce retailers. In 2016, they were the second after Amazon by sales. In e-commerce, the conversion is directly affected by how fast the site loads. For many e-commerce companies, making the site faster by 1 second increased the conversion 1.05, 1.1, or even\u2026","rel":"","context":"In \"performance\"","block_context":{"text":"performance","link":"https:\/\/iamakulov.com\/notes\/tag\/performance\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/iamakulov.com\/notes\/wp-content\/uploads\/2018\/04\/ApplicationFrameHost_2018-03-30_21-12-46.png?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/iamakulov.com\/notes\/wp-content\/uploads\/2018\/04\/ApplicationFrameHost_2018-03-30_21-12-46.png?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/iamakulov.com\/notes\/wp-content\/uploads\/2018\/04\/ApplicationFrameHost_2018-03-30_21-12-46.png?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/iamakulov.com\/notes\/wp-content\/uploads\/2018\/04\/ApplicationFrameHost_2018-03-30_21-12-46.png?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/iamakulov.com\/notes\/wp-content\/uploads\/2018\/04\/ApplicationFrameHost_2018-03-30_21-12-46.png?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/iamakulov.com\/notes\/wp-content\/uploads\/2018\/04\/ApplicationFrameHost_2018-03-30_21-12-46.png?resize=1400%2C800&ssl=1 4x"},"classes":[]}],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/posts\/456","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/comments?post=456"}],"version-history":[{"count":26,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/posts\/456\/revisions"}],"predecessor-version":[{"id":501,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/posts\/456\/revisions\/501"}],"wp:attachment":[{"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/media?parent=456"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/categories?post=456"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/iamakulov.com\/notes\/wp-json\/wp\/v2\/tags?post=456"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}