Auto Added by WPeMatico

Ensuring control over npm dependencies in Kotlin/JS projects

Recently we published a blog post about a potential security issue caused by ua-parser-js, a dependent package that is used in the popular testing framework Karma, which in turn is the default choice for Kotlin/JS and Kotlin Multiplatform applications targeting JS.

In the post, we recommended that you may want to consider locking packages you depend on to a certain version. Here, we want to share with you how you can accomplish that with Kotlin/JS. We recommend everyone to add the snippets below to their Kotlin projects targeting JavaScript.

How npm dependencies are managed

Dependencies from npm are specified in terms of their package name and version (or range). When you execute a Gradle task in a Kotlin/JS project, the plugin downloads and installs dependencies that are required for this particular task (following Gradle’s principle of Task Configuration Avoidance). However, when a dependency version is specified as a range instead of a specific version, this can cause these (possibly transitive) dependencies to be updated automatically.

The package manager used by Kotlin/JS, Yarn, provides a mechanism to lock versions and consequently ensure consistency across multiple installations. It does this via an auto-generated file named yarn.lock, which stores the exact version of all installed dependencies.

For a normal JavaScript project built with Yarn, you use the Yarn CLI to add, upgrade, and remove dependencies. The package manager then takes care of updating the yarn.lock file accordingly, and uses its content during subsequent installations.

However, Kotlin/JS applications specify their dependencies in the build.gradle(.kts) file, instead of using a CLI tool. To still make use of version locking, you need to add some additional Gradle configuration for your project. Let’s see how that is done.

Persisting yarn.lock in your Kotlin/JS projects

To persist the yarn.lock file of your project, add the following Gradle configuration to make sure that all Gradle tasks use the same set of dependencies:

rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
   rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().disableGranularWorkspaces()
}

Add the following snippet to ensure that after running the kotlinNpmInstall Gradle task, a copy of the auto-generated yarn.lock file is stored in the root of the project (named yarn.lock.bak).

Make sure to add the yarn.lock.bak file to your version control system.

Every time your build process calls kotlinNpmInstall, your build then uses the stored yarn.lock.bak in place of an auto-generated yarn.lock, locking the used dependencies in place:

tasks.register("backupYarnLock") {
   dependsOn(":kotlinNpmInstall")

   doLast {
       copy {
           from("$rootDir/build/js/yarn.lock")
           rename { "yarn.lock.bak" }
           into(rootDir)
       }
   }

   inputs.file("$rootDir/build/js/yarn.lock").withPropertyName("inputFile")
   outputs.file("$rootDir/yarn.lock.bak").withPropertyName("outputFile")
}

val restoreYarnLock = tasks.register("restoreYarnLock") {
   doLast {
       copy {
           from("$rootDir/yarn.lock.bak")
           rename { "yarn.lock" }
           into("$rootDir/build/js")
       }
   }

   inputs.file("$rootDir/yarn.lock.bak").withPropertyName("inputFile")
   outputs.file("$rootDir/build/js/yarn.lock").withPropertyName("outputFile")
}

tasks.named("kotlinNpmInstall").configure {
   dependsOn(restoreYarnLock)
}

To validate that your yarn.lock.bak is equal to the yarn.lock file generated from a fresh install, add the following validateYarnLock task to your Gradle build file:

tasks.register("validateYarnLock") {
   dependsOn(":kotlinNpmInstall")

           doLast {
               val expected = file("$rootDir/yarn.lock.bak").readText()
               val actual = file("$rootDir/build/js/yarn.lock").readText()

               if (expected != actual) {
                   throw AssertionError(
                           "Generated yarn.lock differs from the one in the repository. " +
                                   "It can happen because someone has updated a dependency and haven't run `./gradlew :backupYarnLock --refresh-dependencies` " +
                                   "afterwards."
                           )
               }
           }

   inputs.files("$rootDir/yarn.lock.bak", "$rootDir/build/js/yarn.lock").withPropertyName("inputFiles")
}

The snippets above ensure that your dependencies are version-locked. In addition to this, you can disable the execution of Yarn’s lifecycle scripts. This prevents your dependencies from executing code during their installation:

allprojects {
    tasks.withType<KotlinNpmInstallTask> {
        args += "--ignore-scripts"
    }
}

Moving forward

We recommend everyone to add the snippets above to your Kotlin/JS projects to make sure that any dependencies you have – either directly or transitively – are version-locked.

We are also working on providing direct support for persistent yarn.lock files and fine-grained control over Yarn’s lifecycle scripts in Kotlin/JS and Kotlin Multiplatform projects – we suggest following KT-34014 for updates. Our aim is also to make sure that projects maintain compatibility with Gradle’s Task Configuration Avoidance, when the set of dependencies may differ based on the individual Gradle task.

Continue ReadingEnsuring control over npm dependencies in Kotlin/JS projects

Ensuring control over npm dependencies in Kotlin/JS projects

Recently we published a blog post about a potential security issue caused by ua-parser-js, a dependent package that is used in the popular testing framework Karma, which in turn is the default choice for Kotlin/JS and Kotlin Multiplatform applications targeting JS.

In the post, we recommended that you may want to consider locking packages you depend on to a certain version. Here, we want to share with you how you can accomplish that with Kotlin/JS. We recommend everyone to add the snippets below to their Kotlin projects targeting JavaScript.

How npm dependencies are managed

Dependencies from npm are specified in terms of their package name and version (or range). When you execute a Gradle task in a Kotlin/JS project, the plugin downloads and installs dependencies that are required for this particular task (following Gradle’s principle of Task Configuration Avoidance). However, when a dependency version is specified as a range instead of a specific version, this can cause these (possibly transitive) dependencies to be updated automatically.

The package manager used by Kotlin/JS, Yarn, provides a mechanism to lock versions and consequently ensure consistency across multiple installations. It does this via an auto-generated file named yarn.lock, which stores the exact version of all installed dependencies.

For a normal JavaScript project built with Yarn, you use the Yarn CLI to add, upgrade, and remove dependencies. The package manager then takes care of updating the yarn.lock file accordingly, and uses its content during subsequent installations.

However, Kotlin/JS applications specify their dependencies in the build.gradle(.kts) file, instead of using a CLI tool. To still make use of version locking, you need to add some additional Gradle configuration for your project. Let’s see how that is done.

Persisting yarn.lock in your Kotlin/JS projects

To persist the yarn.lock file of your project, add the following Gradle configuration to make sure that all Gradle tasks use the same set of dependencies:

rootProject.plugins.withType(org.jetbrains.kotlin.gradle.targets.js.yarn.YarnPlugin::class.java) {
   rootProject.the<org.jetbrains.kotlin.gradle.targets.js.yarn.YarnRootExtension>().disableGranularWorkspaces()
}

Add the following snippet to ensure that after running the kotlinNpmInstall Gradle task, a copy of the auto-generated yarn.lock file is stored in the root of the project (named yarn.lock.bak).

Make sure to add the yarn.lock.bak file to your version control system.

Every time your build process calls kotlinNpmInstall, your build then uses the stored yarn.lock.bak in place of an auto-generated yarn.lock, locking the used dependencies in place:

tasks.register("backupYarnLock") {
   dependsOn(":kotlinNpmInstall")

   doLast {
       copy {
           from("$rootDir/build/js/yarn.lock")
           rename { "yarn.lock.bak" }
           into(rootDir)
       }
   }

   inputs.file("$rootDir/build/js/yarn.lock").withPropertyName("inputFile")
   outputs.file("$rootDir/yarn.lock.bak").withPropertyName("outputFile")
}

val restoreYarnLock = tasks.register("restoreYarnLock") {
   doLast {
       copy {
           from("$rootDir/yarn.lock.bak")
           rename { "yarn.lock" }
           into("$rootDir/build/js")
       }
   }

   inputs.file("$rootDir/yarn.lock.bak").withPropertyName("inputFile")
   outputs.file("$rootDir/build/js/yarn.lock").withPropertyName("outputFile")
}

tasks.named("kotlinNpmInstall").configure {
   dependsOn(restoreYarnLock)
}

To validate that your yarn.lock.bak is equal to the yarn.lock file generated from a fresh install, add the following validateYarnLock task to your Gradle build file:

tasks.register("validateYarnLock") {
   dependsOn(":kotlinNpmInstall")

           doLast {
               val expected = file("$rootDir/yarn.lock.bak").readText()
               val actual = file("$rootDir/build/js/yarn.lock").readText()

               if (expected != actual) {
                   throw AssertionError(
                           "Generated yarn.lock differs from the one in the repository. " +
                                   "It can happen because someone has updated a dependency and haven't run `./gradlew :backupYarnLock --refresh-dependencies` " +
                                   "afterwards."
                           )
               }
           }

   inputs.files("$rootDir/yarn.lock.bak", "$rootDir/build/js/yarn.lock").withPropertyName("inputFiles")
}

The snippets above ensure that your dependencies are version-locked. In addition to this, you can disable the execution of Yarn’s lifecycle scripts. This prevents your dependencies from executing code during their installation:

allprojects {
    tasks.withType<KotlinNpmInstallTask> {
        args += "--ignore-scripts"
    }
}

Moving forward

We recommend everyone to add the snippets above to your Kotlin/JS projects to make sure that any dependencies you have – either directly or transitively – are version-locked.

We are also working on providing direct support for persistent yarn.lock files and fine-grained control over Yarn’s lifecycle scripts in Kotlin/JS and Kotlin Multiplatform projects – we suggest following KT-34014 for updates. Our aim is also to make sure that projects maintain compatibility with Gradle’s Task Configuration Avoidance, when the set of dependencies may differ based on the individual Gradle task.

Continue ReadingEnsuring control over npm dependencies in Kotlin/JS projects

Important: ua-parser-js exploit and Kotlin/JS

On the 22nd of October at 5:06 pm CEST a message was posted on the #javascript channel of the Kotlin Slack about a potential security issue, at which time we began our investigations. On the 23rd of October, it was reported that malware was found in a very popular npm package named ua-parser-js. The package had been compromised, and a password stealer and cryptocurrency miner installed on target machines. 

Kotlin Multiplatform (targeting JS) and Kotlin/JS use a very popular testing framework named Karma. This in turn has a dependency on the affected package (ua-parser-js). In addition, many developers that target JavaScript, actually use Karma (just like we do on the Kotlin team) as their testing framework. 

As such, we feel it is important for you to be aware of the situation and what you can do to check to see if you’re affected and how to resolve the issue. 

How do I know if I’m affected?

If you’re using directly or indirectly ua-parser-js versions 0.7.29, 0.8.0, and 1.0.0, you can consider your system compromised. This is independently of whether or not you are running tests. Once the dependency is installed, init scripts are automatically launched (unless you executed npm with the --ignore-scripts option or globally had set ignore-scripts true). 

How do I know if I have this package on my system?

There are multiple ways to know if a specific package is installed (either locally or globally) on your system, including:

  • Using npm list command and filtering the output for ua-parser-js 
  • Using npm-check package to check dependencies

A more fail-safe way is to check for the specific `ua-parser-js` on your system, by running the following command

On macOS/Linux 

grep -r --include=package.json '"_id": "ua-parser-js' {x}

On Windows (using Powershell)

Get-ChildItem -Path '{x}' -Recurse -include "package.json" -ErrorAction SilentlyContinue | Select-String -pattern '"_id": "ua-parser-js'

Where {x} is the root folder you want to start the search on. This will output all directories containing the package.json file which uses the package. You can of course search for the specific affected versions instead of all versions if you want. 

It is important to check not only your development environment, but also your build environment or anywhere else you think this package may have been installed (directly or indirectly). 

Also check for any of the following entries during installation of your npm packages (which could be for instance when using karma):

ua-parser-js@0.7.29: this package has been hijacked
ua-parser-js@0.8.0: this package has been hijacked
ua-parser-js@1.0.0: this package has been hijacked

How do I resolve the issue?

Update to a version that is not affected by the issue. The following updates have been released: 0.7.30, 0.8.1 and 1.0.1. Make sure you remove any packages that are affected and rotate any potential credentials that may have been compromised. 

Is it sufficient to remove/update the package?

Given that the package has been compromised to not only perform cryptomining, but also steal credentials, it would be recommended to change any potential credentials that may have been compromised. 

It is also recommended to see if you have any binaries named jsextension or jsextention.exe running, as listed in the comments from the original issue running on your system.

Am I infected even if I haven’t run anything that uses this package?

It is safe to assume that if the package is installed, the system is compromised, something GitHub Advisory also points out.

Was there any time frame for this attack?

It seems that the timeframe in which the attack could have taken place (i.e. from the time the compromised package was updated to the time it was detected) was Oct 22, 2021 at 14:15 CEST to Oct 22, 2021 18:23 CEST. In other words, if none of your packages have updated their dependencies in this short period of time, then most likely you do not have the affected version and consequently most likely not impacted. 

Is Kotlin affected?

As mentioned above, Kotlin Multiplatform (targeting JS) and Kotlin/JS version 1.5.0 and later use a transitive dependency to the affected package. If you ran karma tests for the first time between the hours of Oct 22, 2021 at 14:15 CEST to Oct 22, 2021 18:23 CEST, then you’re most likely affected. 

How can I prevent this from happening in the future?

One recommendation is to use lock files for your dependencies (package-lock.json, yarn.lock, etc) which lock versions of dependencies used, giving you control over any updates, and monitor vulnerabilities of packages you use to be able to timely update them once one of them was compromised. When not using lock files, you may for instance start your application with one version but then during any phase, be it development, build, or deployment, your package manager may update dependencies without you realizing. 

Finally, please note that the story around this vulnerability is still somewhat developing. We’ll post any updates as needed.

Continue ReadingImportant: ua-parser-js exploit and Kotlin/JS

Important: ua-parser-js exploit and Kotlin/JS

On the 22nd of October at 5:06 pm CEST a message was posted on the #javascript channel of the Kotlin Slack about a potential security issue, at which time we began our investigations. On the 23rd of October, it was reported that malware was found in a very popular npm package named ua-parser-js. The package had been compromised, and a password stealer and cryptocurrency miner installed on target machines. 

Kotlin Multiplatform (targeting JS) and Kotlin/JS use a very popular testing framework named Karma. This in turn has a dependency on the affected package (ua-parser-js). In addition, many developers that target JavaScript, actually use Karma (just like we do on the Kotlin team) as their testing framework. 

As such, we feel it is important for you to be aware of the situation and what you can do to check to see if you’re affected and how to resolve the issue. 

How do I know if I’m affected?

If you’re using directly or indirectly ua-parser-js versions 0.7.29, 0.8.0, and 1.0.0, you can consider your system compromised. This is independently of whether or not you are running tests. Once the dependency is installed, init scripts are automatically launched (unless you executed npm with the --ignore-scripts option or globally had set ignore-scripts true). 

How do I know if I have this package on my system?

There are multiple ways to know if a specific package is installed (either locally or globally) on your system, including:

  • Using npm list command and filtering the output for ua-parser-js 
  • Using npm-check package to check dependencies

A more fail-safe way is to check for the specific `ua-parser-js` on your system, by running the following command

On macOS/Linux 

grep -r --include=package.json '"_id": "ua-parser-js' {x}

On Windows (using Powershell)

Get-ChildItem -Path '{x}' -Recurse -include "package.json" -ErrorAction SilentlyContinue | Select-String -pattern '"_id": "ua-parser-js'

Where {x} is the root folder you want to start the search on. This will output all directories containing the package.json file which uses the package. You can of course search for the specific affected versions instead of all versions if you want. 

It is important to check not only your development environment, but also your build environment or anywhere else you think this package may have been installed (directly or indirectly). 

Also check for any of the following entries during installation of your npm packages (which could be for instance when using karma):

ua-parser-js@0.7.29: this package has been hijacked
ua-parser-js@0.8.0: this package has been hijacked
ua-parser-js@1.0.0: this package has been hijacked

How do I resolve the issue?

Update to a version that is not affected by the issue. The following updates have been released: 0.7.30, 0.8.1 and 1.0.1. Make sure you remove any packages that are affected and rotate any potential credentials that may have been compromised. 

Is it sufficient to remove/update the package?

Given that the package has been compromised to not only perform cryptomining, but also steal credentials, it would be recommended to change any potential credentials that may have been compromised. 

It is also recommended to see if you have any binaries named jsextension or jsextention.exe running, as listed in the comments from the original issue running on your system.

Am I infected even if I haven’t run anything that uses this package?

It is safe to assume that if the package is installed, the system is compromised, something GitHub Advisory also points out.

Was there any time frame for this attack?

It seems that the timeframe in which the attack could have taken place (i.e. from the time the compromised package was updated to the time it was detected) was Oct 22, 2021 at 14:15 CEST to Oct 22, 2021 18:23 CEST. In other words, if none of your packages have updated their dependencies in this short period of time, then most likely you do not have the affected version and consequently most likely not impacted. 

Is Kotlin affected?

As mentioned above, Kotlin Multiplatform (targeting JS) and Kotlin/JS version 1.5.0 and later use a transitive dependency to the affected package. If you ran karma tests for the first time between the hours of Oct 22, 2021 at 14:15 CEST to Oct 22, 2021 18:23 CEST, then you’re most likely affected. 

How can I prevent this from happening in the future?

One recommendation is to use lock files for your dependencies (package-lock.json, yarn.lock, etc) which lock versions of dependencies used, giving you control over any updates, and monitor vulnerabilities of packages you use to be able to timely update them once one of them was compromised. When not using lock files, you may for instance start your application with one version but then during any phase, be it development, build, or deployment, your package manager may update dependencies without you realizing.

For instructions on how to use Yarn.lock with Kotlin/JS projects, read our follow-up blog post.

Finally, please note that the story around this vulnerability is still somewhat developing. We’ll post any updates as needed.

Continue ReadingImportant: ua-parser-js exploit and Kotlin/JS

Secure Secrets in Android using Jetpack Security (In-Depth) — Android Security-02

Secure Secrets 🔑 in Android using Jetpack Security (In-Depth) — Android Security-02

In google I/O 2019 Android team released its Security Cryptographic library named Jetpack Security to ease developers in building secured applications.


Secure Secrets 🔑 in Android using Jetpack Security (In-Depth) — Android Security-02 was originally published in Kt. Academy on Medium, where people are continuing the conversation by highlighting and responding to this story.

Continue ReadingSecure Secrets in Android using Jetpack Security (In-Depth) — Android Security-02

How to Secure Secrets in Android-Android Security-01

Security is a key requirement while building an application that’s dealing with APIs, Tokens received from a server, Storing credentials in local DB, Sending secure data onto the network, and many other scenarios.


How to Secure Secrets 🔑 in Android-Android Security-01 was originally published in Kt. Academy on Medium, where people are continuing the conversation by highlighting and responding to this story.

Continue ReadingHow to Secure Secrets in Android-Android Security-01

End of content

No more pages to load