Skip to content

Commit

Permalink
Update the package with new safetydb and new IDE support (#253)
Browse files Browse the repository at this point in the history
* Update the package with new safetydb and new IDE support

* Change the gradle to work around the broken intellij package

* Update a bunch of deprecated API calls
  • Loading branch information
tonybaloney authored Aug 2, 2021
1 parent ae2de01 commit 6377509
Show file tree
Hide file tree
Showing 13 changed files with 13,588 additions and 2,082 deletions.
2 changes: 1 addition & 1 deletion .github/actions/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ FROM anthonypjshaw/pycharm-security:latest
COPY action.sh /action.sh
COPY parse.py /code/parse.py
COPY project.iml /code/project.iml
COPY jdk.table.xml /root/.config/JetBrains/PyCharm2020.2/jdk.table.xml
COPY jdk.table.xml /root/.config/JetBrains/PyCharm2021.2/jdk.table.xml
RUN apt-get -y update && apt-get -y install python3 python3-pip python3-venv && python3 -m pip install setuptools
RUN ["chmod", "+x", "/action.sh"]
ENTRYPOINT ["/action.sh"]
2 changes: 1 addition & 1 deletion .github/actions/jdk.table.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<root url="file:///usr/lib/python3.6/lib-dynload" type="simple" />
<root url="file:///usr/local/lib/python3.6/dist-packages" type="simple" />
<root url="file:///usr/lib/python3/dist-packages" type="simple" />
<root url="file://$USER_HOME$/Library/Caches/JetBrains/PyCharm2020.2/python_stubs/-1506223722" type="simple" />
<root url="file://$USER_HOME$/Library/Caches/JetBrains/PyCharm2021.2/python_stubs/-1506223722" type="simple" />
<root url="file://$APPLICATION_HOME_DIR$/plugins/python/helpers/python-skeletons" type="simple" />
<root url="file://$APPLICATION_HOME_DIR$/plugins/python/helpers/typeshed/stdlib/3.6" type="simple" />
<root url="file://$APPLICATION_HOME_DIR$/plugins/python/helpers/typeshed/stdlib/3" type="simple" />
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
pycharm-version: ['2021.1']
pycharm-version: ['2021.2']
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v1
Expand All @@ -27,7 +27,7 @@ jobs:
java-version: 11
- uses: eskatos/gradle-command-action@v1
with:
arguments: jacocoTestReport -PintellijPublishToken=FAKE_TOKEN -PintellijVersion=2021.1
arguments: jacocoTestReport -PintellijPublishToken=FAKE_TOKEN -PintellijVersion=2021.2
- name: Codecov
uses: codecov/[email protected]
with:
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
ARG PYCHARM_VERSION=2021.1
ARG PYCHARM_VERSION=2021.2
FROM ubuntu:18.04
ARG PYCHARM_VERSION
RUN echo "Building PyCharm $PYCHARM_VERSION with python-security"
Expand Down
5 changes: 5 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Release History

## 1.24.2

* Support for 2021.2 series
* Update safety db to august 2021

## 1.24.1

* Fix a bug raising a runtime exception (PsiInvalidElementException)
Expand Down
23 changes: 12 additions & 11 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ plugins {
}

group 'org.tonybaloney.security'
version '1.24.1'
version '1.24.2'

def ktor_version = "1.6.2"
def kotlin_version = "1.5.21"
Expand Down Expand Up @@ -38,26 +38,27 @@ test {
}

// See https://github.com/JetBrains/gradle-intellij-plugin/
intellij {
type 'PC'
plugins 'python-ce', 'yaml'
updateSinceUntilBuild false
}
// Make the intellij version overridable on the command line to support multiple build versions..
intellij.version = project.hasProperty('intellijVersion') ? project.getProperty('intellijVersion') : '211-EAP-SNAPSHOT'
def intellijversion = project.hasProperty('intellijVersion') ? project.getProperty('intellijVersion') : '2021.2'

intellij {
type = 'PC'
plugins = ['python-ce', 'yaml']
updateSinceUntilBuild = false
version = intellijversion
}

patchPluginXml {
changeNotes """
<h2>1.24.1</h2>
changeNotes = """
<h2>1.24.2</h2>
<ul>
<li>Fix a bug raising a runtime exception (PsiInvalidElementException)</li>
<li>Support for 2021.2 IDEs</li>
</ul>
"""
}

publishPlugin {
token intellijPublishToken
token = intellijPublishToken
// channels 'beta'
}

Expand Down
24 changes: 13 additions & 11 deletions src/main/java/security/packaging/PyPackageSecurityScan.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package security.packaging

import com.google.common.collect.Sets
import com.intellij.notification.NotificationGroup
import com.intellij.notification.NotificationGroupManager
import com.intellij.notification.NotificationListener
import com.intellij.notification.NotificationType
import com.intellij.openapi.module.ModuleManager
Expand All @@ -16,7 +16,7 @@ import kotlinx.coroutines.runBlocking
import security.settings.SecuritySettings

object PyPackageSecurityScan {
var NOTIFICATION_GROUP = NotificationGroup.balloonGroup("Python Package Security Checker")
var NOTIFICATION_GROUP = NotificationGroupManager.getInstance().getNotificationGroup("pythonsecurity.checker")

fun checkPackages(project: Project): Boolean?{
val pythonSdks = getPythonSdks(project)
Expand Down Expand Up @@ -86,43 +86,45 @@ object PyPackageSecurityScan {

private fun backendError(project: Project, message: String?){
NOTIFICATION_GROUP
.createNotification("Could not check Python packages", null,
.createNotification("Could not check Python packages",
"Could not fetch API to validate records. Check your API details.\n$message",
NotificationType.ERROR)
.notify(project)
}

private fun returnError(project: Project){
NOTIFICATION_GROUP
.createNotification("Could not check Python packages", null,
.createNotification("Could not check Python packages",
"Could not verify security of Python packages, unable to locate configured Python Interpreter. Please configure your interpreter.",
NotificationType.INFORMATION)
.notify(project)
}

private fun showTotalIssuesWarning(matches: Int, project: Project) {
NOTIFICATION_GROUP
.createNotification("Completed checking packages", null,
.createNotification("Completed checking packages",
"Found $matches potential security issues with your installed packages.",
NotificationType.WARNING)
.notify(project)
}

private fun showNoMatchesInformation(project: Project) {
NOTIFICATION_GROUP
.createNotification("Completed checking packages", null,
.createNotification("Completed checking packages",
"Found no known security issues with your installed packages.",
NotificationType.INFORMATION)
.notify(project)
}

private fun showFoundIssueWarning(pack: PyPackage?, issue: PackageIssue, project: Project) {
NOTIFICATION_GROUP
.createNotification("Found Security Vulnerability in $pack package", null,
val not = NOTIFICATION_GROUP
.createNotification("Found Security Vulnerability in $pack package",
issue.getMessage(),
NotificationType.WARNING,
NotificationListener.URL_OPENING_LISTENER
).notify(project)
NotificationType.WARNING

)
not.setListener(NotificationListener.URL_OPENING_LISTENER)
not.notify(project)
}

fun getPythonSdks(project: Project): Set<Sdk> {
Expand Down
13 changes: 8 additions & 5 deletions src/main/java/security/packaging/SafetyDbChecker.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package security.packaging

import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import com.jetbrains.python.packaging.PyPackage
import java.io.IOException
Expand Down Expand Up @@ -52,6 +52,8 @@ class SafetyDbChecker : BasePackageChecker {
throw PackageCheckerLoadException(io.message!!)
else
throw PackageCheckerLoadException("Could not load data from SafetyDB API")
}catch (io: com.google.gson.JsonSyntaxException){
throw PackageCheckerLoadException("Could not load data from SafetyDB API, JSON file is corrupted")
}
}

Expand All @@ -60,16 +62,17 @@ class SafetyDbChecker : BasePackageChecker {
}

private fun load(databaseReader: Reader, lookupReader: Reader) {
val gson = GsonBuilder().create()
val recordLookupType = object : TypeToken<Map<String?, List<String>>>() {}.type
lookup = Gson().fromJson(lookupReader, recordLookupType)
lookup = gson.fromJson(lookupReader, recordLookupType)

val recordDatabaseType = object : TypeToken<Map<String?, List<SafetyDbRecord>>>() {}.type
database = Gson().fromJson(databaseReader, recordDatabaseType)
database = gson.fromJson(databaseReader, recordDatabaseType)
}

override fun hasMatch(pythonPackage: PyPackage?): Boolean{
if (pythonPackage==null) return false
for (record in lookup[pythonPackage.name.toLowerCase()] ?: return false){
for (record in lookup[pythonPackage.name.lowercase()] ?: return false){
val specs = parseVersionSpecs(record) ?: continue
if (specs.all { it != null && it.matches(pythonPackage.version) })
return true
Expand All @@ -80,7 +83,7 @@ class SafetyDbChecker : BasePackageChecker {
override suspend fun getMatches (pythonPackage: PyPackage?): List<SafetyDbIssue> {
if (pythonPackage==null) return listOf()
val records: ArrayList<SafetyDbIssue> = ArrayList()
for (record in database[pythonPackage.name.toLowerCase()] ?: error("Package not in database")){
for (record in database[pythonPackage.name.lowercase()] ?: error("Package not in database")){
val specs = parseVersionSpecs(record.v) ?: continue
if (specs.all { it != null && it.matches(pythonPackage.version) })
records.add(SafetyDbIssue(record, pythonPackage))
Expand Down
18 changes: 7 additions & 11 deletions src/main/java/security/packaging/SnykChecker.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
package security.packaging

import com.jetbrains.python.packaging.PyPackage
import io.ktor.client.HttpClient
import io.ktor.client.engine.apache.Apache
import io.ktor.client.features.ServerResponseException
import io.ktor.client.features.defaultRequest
import io.ktor.client.features.json.GsonSerializer
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.request.get
import io.ktor.client.request.header
import io.ktor.client.request.headers
import io.ktor.http.Url
import io.ktor.client.*
import io.ktor.client.engine.apache.*
import io.ktor.client.features.*
import io.ktor.client.features.json.*
import io.ktor.client.request.*
import io.ktor.http.*
import kotlinx.coroutines.TimeoutCancellationException
import java.net.SocketTimeoutException

Expand Down Expand Up @@ -95,7 +91,7 @@ class SnykChecker (private val apiKey: String, private val orgId: String ): Base
override suspend fun getMatches (pythonPackage: PyPackage?): List<SnykIssue> {
if (pythonPackage==null) return listOf()
val records: ArrayList<SnykIssue> = ArrayList()
val data = load(pythonPackage.name.toLowerCase(), pythonPackage.version) ?: return records
val data = load(pythonPackage.name.lowercase(), pythonPackage.version) ?: return records
if (data.ok) return records
if (data.issues == null) return records

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/security/validators/SqlInjectionInspection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,11 @@ class SqlInjectionInspection : PyInspection() {

fun looksLikeSql(str: String) : Boolean {
// Quickly respond to double-worded SQL statements
if (certainlySqlStartingStrings.any { str.toUpperCase().startsWith(it) }) return true
if (certainlySqlStartingStrings.any { str.uppercase().startsWith(it) }) return true

// SELECT must contain FROM, and UPDATE must contain SET
possiblySqlCommandPairs.forEach { pair ->
if (str.toUpperCase().startsWith(pair.key) && str.toUpperCase().contains(pair.value))
if (str.uppercase().startsWith(pair.key) && str.uppercase().contains(pair.value))
return true
}
return false
Expand Down
7 changes: 6 additions & 1 deletion src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<li>Scan code in your CI/CD using Docker</li>
</ul>
]]></description>
<idea-version since-build="201.5985.*" until-build="211.*" />
<idea-version since-build="201.5985.*" until-build="212.*" />

<!-- please see http://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/plugin_compatibility.html
on how to target different products -->
Expand Down Expand Up @@ -69,6 +69,10 @@

<applicationConfigurable displayName="Python Security" instance="security.settings.SecuritySettingsConfigurable" id="org.tonybaloney.security.pycharm-security.settings.SecuritySettingsConfigurable" groupId="tools"/>
<applicationService serviceImplementation="security.settings.SecuritySettings" id="org.tonybaloney.security.pycharm-security.settings.SecuritySettings" />

<notificationGroup displayType="BALLOON" id="pythonsecurity.checker" >

</notificationGroup>
</extensions>
<extensions defaultExtensionNs="Pythonid">
</extensions>
Expand All @@ -77,4 +81,5 @@
<add-to-group group-id="ToolsMenu" anchor="last"/>
</action>
</actions>

</idea-plugin>
Loading

0 comments on commit 6377509

Please sign in to comment.