Merge branch 'main' into #24-switch-to-mautrix

This commit is contained in:
Cubicroot 2022-03-06 17:32:29 +01:00 committed by GitHub
commit dc6c76747f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 777 additions and 62 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
layout_poetry

View file

@ -31,6 +31,9 @@ jobs:
with: with:
go-version: 1.16 go-version: 1.16
- name: Install Poetry
run: curl -sSL https://install.python-poetry.org | python3 -
- name: Install dependencies - name: Install dependencies
run: make setup run: make setup

View file

@ -1,4 +1,5 @@
name: Main name: Main
on: on:
push: push:
branches: branches:
@ -7,6 +8,7 @@ on:
- '**' - '**'
- '!v[0-9]+.[0-9]+.[0-9]+' - '!v[0-9]+.[0-9]+.[0-9]+'
pull_request: pull_request:
jobs: jobs:
test_build: test_build:
name: Test and build name: Test and build
@ -14,11 +16,19 @@ jobs:
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Export GOBIN - name: Export GOBIN
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.16 go-version: 1.16
- name: Install Poetry
run: curl -sSL https://install.python-poetry.org | python3 -
- name: Install dependencies - name: Install dependencies
run: make setup run: make setup
- name: Run tests - name: Run tests
run: make test run: |
source $(poetry env info --path)/bin/activate
make test

View file

@ -25,11 +25,16 @@ jobs:
with: with:
go-version: 1.16 go-version: 1.16
- name: Install Poetry
run: curl -sSL https://install.python-poetry.org | python3 -
- name: Install dependencies - name: Install dependencies
run: make setup run: make setup
- name: Run tests - name: Run tests
run: make test run: |
source $(poetry env info --path)/bin/activate
make test
- name: Log in to the Container registry - name: Log in to the Container registry
uses: docker/login-action@v1 uses: docker/login-action@v1
@ -53,3 +58,12 @@ jobs:
push: true push: true
tags: ${{ steps.meta.outputs.tags }} tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }} labels: ${{ steps.meta.outputs.labels }}
- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v2
with:
distribution: goreleaser
version: latest
args: release --rm-dist
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

151
.gitignore vendored
View file

@ -1,6 +1,7 @@
out/ out/
*.db *.db
config.yml config.yml
docs/
### Go ### Go
# Binaries for programs and plugins # Binaries for programs and plugins
@ -19,8 +20,154 @@ config.yml
# Dependency directories (remove the comment below to include it) # Dependency directories (remove the comment below to include it)
# vendor/ # vendor/
### Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
### VisualStudioCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace *.code-workspace
# Build documentation # Local History for Visual Studio Code
.history/
docs/

3
.gitmodules vendored Normal file
View file

@ -0,0 +1,3 @@
[submodule "tests/semgrep-rules"]
path = tests/semgrep-rules
url = https://github.com/returntocorp/semgrep-rules

19
.goreleaser.yml Normal file
View file

@ -0,0 +1,19 @@
builds:
- id: pushbits
main: ./cmd/pushbits
goos:
- linux
goarch:
- amd64
- 386
- arm
- arm64
checksum:
algorithm: sha256
archives:
- id: pushbits
builds:
- pushbits
format: tar.gz

View file

@ -1,23 +1,45 @@
# References:
# [1] Needed so the Go files of semgrep-rules do not interfere with static analysis
DOCS_DIR := ./docs
OUT_DIR := ./out
TESTS_DIR := ./tests
SEMGREP_MODFILE := $(TESTS_DIR)/semgrep-rules/go.mod
.PHONY: build .PHONY: build
build: build:
mkdir -p ./out mkdir -p $(OUT_DIR)
go build -ldflags="-w -s" -o ./out/pushbits ./cmd/pushbits go build -ldflags="-w -s" -o $(OUT_DIR)/pushbits ./cmd/pushbits
.PHONY: clean
clean:
rm -rf $(DOCS_DIR)
rm -rf $(OUT_DIR)
rm -rf $(SEMGREP_MODFILE)
.PHONY: test .PHONY: test
test: test:
stdout=$$(gofmt -l . 2>&1); if [ "$$stdout" ]; then exit 1; fi touch $(SEMGREP_MODFILE) # See [1].
go fmt ./...
go vet ./... go vet ./...
gocyclo -over 10 $(shell find . -iname '*.go' -type f) gocyclo -over 10 $(shell find . -type f \( -iname '*.go' ! -path "./tests/semgrep-rules/*" \))
staticcheck ./... staticcheck ./...
go test -v -cover ./... go test -v -cover ./...
gosec -exclude-dir=tests ./...
semgrep --lang=go --config=tests/semgrep-rules/go --metrics=off
rm -rf $(SEMGREP_MODFILE) # See [1].
@printf '\n%s\n' "> Test successful" @printf '\n%s\n' "> Test successful"
.PHONY: setup .PHONY: setup
setup: setup:
git submodule update --init --recursive
go install github.com/fzipp/gocyclo/cmd/gocyclo@latest go install github.com/fzipp/gocyclo/cmd/gocyclo@latest
go install github.com/securego/gosec/v2/cmd/gosec@latest
go install github.com/swaggo/swag/cmd/swag@latest go install github.com/swaggo/swag/cmd/swag@latest
go install honnef.co/go/tools/cmd/staticcheck@latest go install honnef.co/go/tools/cmd/staticcheck@latest
poetry install
.PHONY: swag .PHONY: swag
swag: swag:
swag init --parseDependency=true -d . -g cmd/pushbits/main.go swag init --parseDependency=true --exclude $(TESTS_DIR) -g cmd/pushbits/main.go

View file

@ -9,9 +9,7 @@
<div align="center"> <div align="center">
<h1>PushBits</h1> <h1>PushBits</h1>
<h4 align="center"> <p align="center"><b>Receive your important notifications immediately, over <a href="https://matrix.org/">Matrix</a>.</b></p>
Receive your important notifications immediately, over <a href="https://matrix.org/">Matrix</a>.
</h4>
<p>PushBits enables you to send push notifications via a simple web API, and delivers them to your users.</p> <p>PushBits enables you to send push notifications via a simple web API, and delivers them to your users.</p>
</div> </div>
@ -31,21 +29,7 @@ It enables you to send notifications via a simple web API, and delivers them to
This is similar to what [Pushover](https://pushover.net/) and [Gotify](https://gotify.net/) offer, but it does not require an additional app. This is similar to what [Pushover](https://pushover.net/) and [Gotify](https://gotify.net/) offer, but it does not require an additional app.
The vision is to have compatibility with Gotify on the sending side, while on the receiving side an established service is used. The vision is to have compatibility with Gotify on the sending side, while on the receiving side an established service is used.
This has the advantages that This has the advantages that we need to maintain neither plugins (like those for [Watchtower](https://containrrr.dev/watchtower/) and [Jellyfin](https://jellyfin.org/)) nor clients.
- sending plugins written for Gotify (like those for [Watchtower](https://containrrr.dev/watchtower/) and [Jellyfin](https://jellyfin.org/)) as well as
- receiving clients written for Matrix
can be reused.
### Why Matrix instead of X?
This project totally would've used Signal if it would offer a proper API.
Sadly, neither [Signal](https://signal.org/) nor [WhatsApp](https://www.whatsapp.com/) come with an API (at the time of writing) through which PushBits could interact.
In [Telegram](https://telegram.org/) there is an API to run bots, but these are limited in that they cannot create chats by themselves.
If you insist on going with Telegram, have a look at [webhook2telegram](https://github.com/muety/webhook2telegram).
The idea of a federated, synchronized but yet end-to-end encrypted protocol is awesome, but its clients simply aren't really there yet.
Still, if you haven't tried it yet, we'd encourage you to check it out.
## 🤘&nbsp;Features ## 🤘&nbsp;Features
@ -57,18 +41,38 @@ Still, if you haven't tried it yet, we'd encourage you to check it out.
- [ ] Two-factor authentication, [issue](https://github.com/pushbits/server/issues/19) - [ ] Two-factor authentication, [issue](https://github.com/pushbits/server/issues/19)
- [ ] Bi-directional key verification, [issue](https://github.com/pushbits/server/issues/20) - [ ] Bi-directional key verification, [issue](https://github.com/pushbits/server/issues/20)
## 👮&nbsp;Acknowledgments ## 👮&nbsp;License and Acknowledgments
The idea for this software and most parts of the initial source are heavily inspired by [Gotify](https://gotify.net/). Please refer to [the LICENSE file](LICENSE) to learn more about the license of this code.
Many thanks to [jmattheis](https://jmattheis.de/) for his well-structured code. It applies only where not specified differently.
## 💻&nbsp;Development The idea for this software was inspired by [Gotify](https://gotify.net/).
The source code is located on [GitHub](https://github.com/pushbits/server). ## 💻&nbsp;Development and Contributions
You can retrieve it by checking out the repository as follows.
The source code is located [on GitHub](https://github.com/pushbits/server).
You can retrieve it by checking out the repository as follows:
```bash ```bash
git clone https://github.com/pushbits/server.git git clone https://github.com/pushbits/server.git
``` ```
[![Stargazers over time](https://starchart.cc/pushbits/server.svg)](https://starchart.cc/pushbits/server) :wrench: **Want to contribute?**
Before moving forward, please refer to [our contribution guidelines](CONTRIBUTING.md).
:mailbox: **Found a security vulnerability?**
Check [this document](SECURITY.md) for information on how you can bring it to our attention.
:star: **Like fancy graphs?** See [our stargazers over time](https://starchart.cc/pushbits/server).
## ❓&nbsp;Frequently Asked Questions (FAQ)
### Why Matrix instead of X?
This project totally would've used Signal if it would offer a proper API.
Sadly, neither [Signal](https://signal.org/) nor [WhatsApp](https://www.whatsapp.com/) come with an API (at the time of writing) through which PushBits could interact.
In [Telegram](https://telegram.org/) there is an API to run bots, but these are limited in that they cannot create chats by themselves.
If you insist on going with Telegram, have a look at [webhook2telegram](https://github.com/muety/webhook2telegram).
The idea of a federated, synchronized but yet end-to-end encrypted protocol is awesome, but its clients simply aren't really there yet.
Still, if you haven't tried it yet, we'd encourage you to check it out.

View file

@ -2,7 +2,7 @@
## Supported Versions ## Supported Versions
Only the latest version is supported. Because we are a small team, only the latest version is supported.
## Reporting a Vulnerability ## Reporting a Vulnerability

View file

@ -77,5 +77,8 @@ func main() {
engine := router.Create(c.Debug, cm, db, dp) engine := router.Create(c.Debug, cm, db, dp)
runner.Run(engine, c.HTTP.ListenAddress, c.HTTP.Port) err = runner.Run(engine, c.HTTP.ListenAddress, c.HTTP.Port)
if err != nil {
log.Fatal(err)
}
} }

View file

@ -35,7 +35,11 @@ func (h *ApplicationHandler) registerApplication(ctx *gin.Context, a *model.Appl
} }
a.MatrixID = channelID a.MatrixID = channelID
h.DB.UpdateApplication(a)
err = h.DB.UpdateApplication(a)
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
return err
}
return nil return nil
} }
@ -55,7 +59,6 @@ func (h *ApplicationHandler) createApplication(ctx *gin.Context, u *model.User,
if err := h.registerApplication(ctx, &application, u); err != nil { if err := h.registerApplication(ctx, &application, u); err != nil {
err := h.DB.DeleteApplication(&application) err := h.DB.DeleteApplication(&application)
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success { if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
log.Printf("Cannot delete application with ID %d.", application.ID) log.Printf("Cannot delete application with ID %d.", application.ID)
} }

View file

@ -4,7 +4,6 @@ import (
"log" "log"
"net/http" "net/http"
"net/url" "net/url"
"strings"
"time" "time"
"github.com/pushbits/server/internal/authentication" "github.com/pushbits/server/internal/authentication"
@ -45,23 +44,17 @@ type NotificationHandler struct {
// @Failure 500,404,403 "" // @Failure 500,404,403 ""
// @Router /message [post] // @Router /message [post]
func (h *NotificationHandler) CreateNotification(ctx *gin.Context) { func (h *NotificationHandler) CreateNotification(ctx *gin.Context) {
var notification model.Notification application := authentication.GetApplication(ctx)
log.Printf("Sending notification for application %s.", application.Name)
var notification model.Notification
if err := ctx.Bind(&notification); err != nil { if err := ctx.Bind(&notification); err != nil {
return return
} }
application := authentication.GetApplication(ctx) notification.Sanitize(application)
log.Printf("Sending notification for application %s.", application.Name)
notification.ApplicationID = application.ID
if strings.TrimSpace(notification.Title) == "" {
notification.Title = application.Name
}
notification.Date = time.Now()
messageID, err := h.DP.SendNotification(application, &notification) messageID, err := h.DP.SendNotification(application, &notification)
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success { if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
return return
} }
@ -86,8 +79,9 @@ func (h *NotificationHandler) CreateNotification(ctx *gin.Context) {
// @Router /message/{message_id} [DELETE] // @Router /message/{message_id} [DELETE]
func (h *NotificationHandler) DeleteNotification(ctx *gin.Context) { func (h *NotificationHandler) DeleteNotification(ctx *gin.Context) {
application := authentication.GetApplication(ctx) application := authentication.GetApplication(ctx)
id, err := getMessageID(ctx) log.Printf("Deleting notification for application %s.", application.Name)
id, err := getMessageID(ctx)
if success := successOrAbort(ctx, http.StatusUnprocessableEntity, err); !success { if success := successOrAbort(ctx, http.StatusUnprocessableEntity, err); !success {
return return
} }

View file

@ -44,6 +44,8 @@ func (h *UserHandler) deleteApplications(ctx *gin.Context, u *model.User) error
} }
for _, application := range applications { for _, application := range applications {
application := application // See https://stackoverflow.com/a/68247837
if err := h.AH.deleteApplication(ctx, &application, u); err != nil { if err := h.AH.deleteApplication(ctx, &application, u); err != nil {
return err return err
} }
@ -59,6 +61,8 @@ func (h *UserHandler) updateChannels(ctx *gin.Context, u *model.User, matrixID s
} }
for _, application := range applications { for _, application := range applications {
application := application // See https://stackoverflow.com/a/68247837
err := h.DP.DeregisterApplication(&application, u) err := h.DP.DeregisterApplication(&application, u)
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success { if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
return err return err
@ -68,6 +72,8 @@ func (h *UserHandler) updateChannels(ctx *gin.Context, u *model.User, matrixID s
u.MatrixID = matrixID u.MatrixID = matrixID
for _, application := range applications { for _, application := range applications {
application := application // See https://stackoverflow.com/a/68247837
err := h.AH.registerApplication(ctx, &application, u) err := h.AH.registerApplication(ctx, &application, u)
if err != nil { if err != nil {
return err return err
@ -169,10 +175,10 @@ func (h *UserHandler) GetUsers(ctx *gin.Context) {
return return
} }
var externalUsers []*model.ExternalUser externalUsers := make([]*model.ExternalUser, len(users))
for _, user := range users { for i, user := range users {
externalUsers = append(externalUsers, user.IntoExternalUser()) externalUsers[i] = user.IntoExternalUser()
} }
ctx.JSON(http.StatusOK, &externalUsers) ctx.JSON(http.StatusOK, &externalUsers)

View file

@ -1,7 +1,7 @@
package credentials package credentials
import ( import (
"crypto/sha1" "crypto/sha1" //#nosec G505 -- False positive, see the use below.
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"log" "log"
@ -21,7 +21,8 @@ func IsPasswordPwned(password string) (bool, error) {
return true, nil return true, nil
} }
hash := sha1.Sum([]byte(password)) // nosemgrep: tests.semgrep-rules.go.lang.security.audit.crypto.insecure-module-used, tests.semgrep-rules.go.lang.security.audit.crypto.use-of-sha1
hash := sha1.Sum([]byte(password)) //#nosec G401 -- False positive, only the first 5 bytes are transmitted.
hashStr := fmt.Sprintf("%X", hash) hashStr := fmt.Sprintf("%X", hash)
lookup := hashStr[0:5] lookup := hashStr[0:5]
match := hashStr[5:] match := hashStr[5:]

View file

@ -24,8 +24,11 @@ type Database struct {
} }
func createFileDir(file string) { func createFileDir(file string) {
if _, err := os.Stat(filepath.Dir(file)); os.IsNotExist(err) { dir := filepath.Dir(file)
if err := os.MkdirAll(filepath.Dir(file), 0775); err != nil {
if _, err := os.Stat(dir); os.IsNotExist(err) {
// nosemgrep: tests.semgrep-rules.go.lang.correctness.permissions.incorrect-default-permission
if err := os.MkdirAll(dir, 0750); err != nil {
panic(err) panic(err)
} }
} }
@ -67,14 +70,20 @@ func Create(cm *credentials.Manager, dialect, connection string) (*Database, err
sql.SetConnMaxLifetime(9 * time.Minute) sql.SetConnMaxLifetime(9 * time.Minute)
} }
db.AutoMigrate(&model.User{}, &model.Application{}) err = db.AutoMigrate(&model.User{}, &model.Application{})
if err != nil {
return nil, err
}
return &Database{gormdb: db, sqldb: sql, credentialsManager: cm}, nil return &Database{gormdb: db, sqldb: sql, credentialsManager: cm}, nil
} }
// Close closes the database connection. // Close closes the database connection.
func (d *Database) Close() { func (d *Database) Close() {
d.sqldb.Close() err := d.sqldb.Close()
if err != nil {
log.Printf("Error while closing database: %s", err)
}
} }
// Populate fills the database with initial information like the admin user. // Populate fills the database with initial information like the admin user.
@ -111,12 +120,16 @@ func (d *Database) RepairChannels(dp Dispatcher) error {
} }
for _, user := range users { for _, user := range users {
user := user // See https://stackoverflow.com/a/68247837
applications, err := d.GetApplications(&user) applications, err := d.GetApplications(&user)
if err != nil { if err != nil {
return err return err
} }
for _, application := range applications { for _, application := range applications {
application := application // See https://stackoverflow.com/a/68247837
if err := dp.UpdateApplication(&application); err != nil { if err := dp.UpdateApplication(&application); err != nil {
return err return err
} }

View file

@ -41,7 +41,11 @@ func Create(homeserver, username, password string, formatting configuration.Form
func (d *Dispatcher) Close() { func (d *Dispatcher) Close() {
log.Printf("Logging out.") log.Printf("Logging out.")
d.mautrixClient.Logout() _, err := d.mautrixClient.Logout()
if err != nil {
log.Printf("Error while logging out: %s", err)
}
d.mautrixClient.ClearCredentials() d.mautrixClient.ClearCredentials()
log.Printf("Successfully logged out.") log.Printf("Successfully logged out.")

View file

@ -1,6 +1,7 @@
package model package model
import ( import (
"strings"
"time" "time"
) )
@ -16,6 +17,17 @@ type Notification struct {
Date time.Time `json:"date"` Date time.Time `json:"date"`
} }
// Sanitize sets explicit defaults for a notification.
func (n *Notification) Sanitize(application *Application) {
n.ID = ""
n.UrlEncodedID = ""
n.ApplicationID = application.ID
if strings.TrimSpace(n.Title) == "" {
n.Title = application.Name
}
n.Date = time.Now()
}
// DeleteNotification holds information like the message ID of a deletion notification. // DeleteNotification holds information like the message ID of a deletion notification.
type DeleteNotification struct { type DeleteNotification struct {
ID string `json:"id" form:"id"` ID string `json:"id" form:"id"`

View file

@ -7,6 +7,11 @@ import (
) )
// Run starts the Gin engine. // Run starts the Gin engine.
func Run(engine *gin.Engine, address string, port int) { func Run(engine *gin.Engine, address string, port int) error {
engine.Run(fmt.Sprintf("%s:%d", address, port)) err := engine.Run(fmt.Sprintf("%s:%d", address, port))
if err != nil {
return err
}
return nil
} }

439
poetry.lock generated Normal file
View file

@ -0,0 +1,439 @@
[[package]]
name = "attrs"
version = "21.4.0"
description = "Classes Without Boilerplate"
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[package.extras]
dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"]
docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
[[package]]
name = "bracex"
version = "2.2.1"
description = "Bash style brace expander."
category = "dev"
optional = false
python-versions = ">=3.6"
[[package]]
name = "certifi"
version = "2021.10.8"
description = "Python package for providing Mozilla's CA Bundle."
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "charset-normalizer"
version = "2.0.12"
description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet."
category = "dev"
optional = false
python-versions = ">=3.5.0"
[package.extras]
unicode_backport = ["unicodedata2"]
[[package]]
name = "click"
version = "8.0.3"
description = "Composable command line interface toolkit"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
[[package]]
name = "click-option-group"
version = "0.5.3"
description = "Option groups missing in Click"
category = "dev"
optional = false
python-versions = ">=3.6,<4"
[package.dependencies]
Click = ">=7.0,<9"
[package.extras]
docs = ["sphinx (>=2.3,<3)", "pallets-sphinx-themes", "m2r"]
tests = ["coverage (<6)", "pytest", "pytest-cov", "coveralls"]
[[package]]
name = "colorama"
version = "0.4.4"
description = "Cross-platform colored terminal text."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "idna"
version = "3.3"
description = "Internationalized Domain Names in Applications (IDNA)"
category = "dev"
optional = false
python-versions = ">=3.5"
[[package]]
name = "importlib-metadata"
version = "4.11.0"
description = "Read metadata from Python packages"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
typing-extensions = {version = ">=3.6.4", markers = "python_version < \"3.8\""}
zipp = ">=0.5"
[package.extras]
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
perf = ["ipython"]
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"]
[[package]]
name = "importlib-resources"
version = "5.4.0"
description = "Read resources from Python packages"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""}
[package.extras]
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"]
[[package]]
name = "jsonschema"
version = "4.4.0"
description = "An implementation of JSON Schema validation for Python"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
attrs = ">=17.4.0"
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
importlib-resources = {version = ">=1.4.0", markers = "python_version < \"3.9\""}
pyrsistent = ">=0.14.0,<0.17.0 || >0.17.0,<0.17.1 || >0.17.1,<0.17.2 || >0.17.2"
typing-extensions = {version = "*", markers = "python_version < \"3.8\""}
[package.extras]
format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"]
format_nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"]
[[package]]
name = "packaging"
version = "21.3"
description = "Core utilities for Python packages"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
pyparsing = ">=2.0.2,<3.0.5 || >3.0.5"
[[package]]
name = "peewee"
version = "3.14.8"
description = "a little orm"
category = "dev"
optional = false
python-versions = "*"
[[package]]
name = "pyparsing"
version = "3.0.7"
description = "Python parsing module"
category = "dev"
optional = false
python-versions = ">=3.6"
[package.extras]
diagrams = ["jinja2", "railroad-diagrams"]
[[package]]
name = "pyrsistent"
version = "0.18.1"
description = "Persistent/Functional/Immutable data structures"
category = "dev"
optional = false
python-versions = ">=3.7"
[[package]]
name = "requests"
version = "2.27.1"
description = "Python HTTP for Humans."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
[package.dependencies]
certifi = ">=2017.4.17"
charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""}
idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""}
urllib3 = ">=1.21.1,<1.27"
[package.extras]
socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"]
[[package]]
name = "ruamel.yaml"
version = "0.17.21"
description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order"
category = "dev"
optional = false
python-versions = ">=3"
[package.dependencies]
"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""}
[package.extras]
docs = ["ryd"]
jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"]
[[package]]
name = "ruamel.yaml.clib"
version = "0.2.6"
description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml"
category = "dev"
optional = false
python-versions = ">=3.5"
[[package]]
name = "semgrep"
version = "0.82.0"
description = "Lightweight static analysis for many languages. Find bug variants with patterns that look like source code."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
attrs = ">=19.3.0"
click = ">=8.0.1"
click-option-group = ">=0.5.3"
colorama = ">=0.4.3"
jsonschema = ">=3.2.0,<5"
packaging = ">=20.4"
peewee = ">=3.14.4,<3.15.0"
requests = ">=2.22.0"
"ruamel.yaml" = ">=0.16.0,<0.18"
tqdm = ">=4.46.1"
wcmatch = "8.3"
[[package]]
name = "tqdm"
version = "4.62.3"
description = "Fast, Extensible Progress Meter"
category = "dev"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
[package.dependencies]
colorama = {version = "*", markers = "platform_system == \"Windows\""}
[package.extras]
dev = ["py-make (>=0.1.0)", "twine", "wheel"]
notebook = ["ipywidgets (>=6)"]
telegram = ["requests"]
[[package]]
name = "typing-extensions"
version = "4.1.0"
description = "Backported and Experimental Type Hints for Python 3.6+"
category = "dev"
optional = false
python-versions = ">=3.6"
[[package]]
name = "urllib3"
version = "1.26.8"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "dev"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
[package.extras]
brotli = ["brotlipy (>=0.6.0)"]
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[[package]]
name = "wcmatch"
version = "8.3"
description = "Wildcard/glob file name matcher."
category = "dev"
optional = false
python-versions = ">=3.6"
[package.dependencies]
bracex = ">=2.1.1"
[[package]]
name = "zipp"
version = "3.7.0"
description = "Backport of pathlib-compatible object wrapper for zip files"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.extras]
docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
[metadata]
lock-version = "1.1"
python-versions = "^3.7"
content-hash = "792ec3aa3a1641a70a6cd82c2399211c85c8a5cad5522f260c86ef407f8482ce"
[metadata.files]
attrs = [
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
]
bracex = [
{file = "bracex-2.2.1-py3-none-any.whl", hash = "sha256:096c4b788bf492f7af4e90ef8b5bcbfb99759ae3415ea1b83c9d29a5ed8f9a94"},
{file = "bracex-2.2.1.tar.gz", hash = "sha256:1c8d1296e00ad9a91030ccb4c291f9e4dc7c054f12c707ba3c5ff3e9a81bcd21"},
]
certifi = [
{file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
{file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
]
charset-normalizer = [
{file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"},
{file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"},
]
click = [
{file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"},
{file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"},
]
click-option-group = [
{file = "click-option-group-0.5.3.tar.gz", hash = "sha256:a6e924f3c46b657feb5b72679f7e930f8e5b224b766ab35c91ae4019b4e0615e"},
{file = "click_option_group-0.5.3-py3-none-any.whl", hash = "sha256:9653a2297357335d7325a1827e71ac1245d91c97d959346a7decabd4a52d5354"},
]
colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
]
idna = [
{file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"},
{file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"},
]
importlib-metadata = [
{file = "importlib_metadata-4.11.0-py3-none-any.whl", hash = "sha256:6affcdb3aec542dd98df8211e730bba6c5f2bec8288d47bacacde898f548c9ad"},
{file = "importlib_metadata-4.11.0.tar.gz", hash = "sha256:9e5e553bbba1843cb4a00823014b907616be46ee503d2b9ba001d214a8da218f"},
]
importlib-resources = [
{file = "importlib_resources-5.4.0-py3-none-any.whl", hash = "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45"},
{file = "importlib_resources-5.4.0.tar.gz", hash = "sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b"},
]
jsonschema = [
{file = "jsonschema-4.4.0-py3-none-any.whl", hash = "sha256:77281a1f71684953ee8b3d488371b162419767973789272434bbc3f29d9c8823"},
{file = "jsonschema-4.4.0.tar.gz", hash = "sha256:636694eb41b3535ed608fe04129f26542b59ed99808b4f688aa32dcf55317a83"},
]
packaging = [
{file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"},
{file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"},
]
peewee = [
{file = "peewee-3.14.8.tar.gz", hash = "sha256:01bd7f734defb08d7a3346a0c0ca7011bc8d0d685934ec0e001b3371d522ec53"},
]
pyparsing = [
{file = "pyparsing-3.0.7-py3-none-any.whl", hash = "sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484"},
{file = "pyparsing-3.0.7.tar.gz", hash = "sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea"},
]
pyrsistent = [
{file = "pyrsistent-0.18.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1"},
{file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26"},
{file = "pyrsistent-0.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e"},
{file = "pyrsistent-0.18.1-cp310-cp310-win32.whl", hash = "sha256:e4f3149fd5eb9b285d6bfb54d2e5173f6a116fe19172686797c056672689daf6"},
{file = "pyrsistent-0.18.1-cp310-cp310-win_amd64.whl", hash = "sha256:636ce2dc235046ccd3d8c56a7ad54e99d5c1cd0ef07d9ae847306c91d11b5fec"},
{file = "pyrsistent-0.18.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e92a52c166426efbe0d1ec1332ee9119b6d32fc1f0bbfd55d5c1088070e7fc1b"},
{file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7a096646eab884bf8bed965bad63ea327e0d0c38989fc83c5ea7b8a87037bfc"},
{file = "pyrsistent-0.18.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cdfd2c361b8a8e5d9499b9082b501c452ade8bbf42aef97ea04854f4a3f43b22"},
{file = "pyrsistent-0.18.1-cp37-cp37m-win32.whl", hash = "sha256:7ec335fc998faa4febe75cc5268a9eac0478b3f681602c1f27befaf2a1abe1d8"},
{file = "pyrsistent-0.18.1-cp37-cp37m-win_amd64.whl", hash = "sha256:6455fc599df93d1f60e1c5c4fe471499f08d190d57eca040c0ea182301321286"},
{file = "pyrsistent-0.18.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:fd8da6d0124efa2f67d86fa70c851022f87c98e205f0594e1fae044e7119a5a6"},
{file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bfe2388663fd18bd8ce7db2c91c7400bf3e1a9e8bd7d63bf7e77d39051b85ec"},
{file = "pyrsistent-0.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e3e1fcc45199df76053026a51cc59ab2ea3fc7c094c6627e93b7b44cdae2c8c"},
{file = "pyrsistent-0.18.1-cp38-cp38-win32.whl", hash = "sha256:b568f35ad53a7b07ed9b1b2bae09eb15cdd671a5ba5d2c66caee40dbf91c68ca"},
{file = "pyrsistent-0.18.1-cp38-cp38-win_amd64.whl", hash = "sha256:d1b96547410f76078eaf66d282ddca2e4baae8964364abb4f4dcdde855cd123a"},
{file = "pyrsistent-0.18.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f87cc2863ef33c709e237d4b5f4502a62a00fab450c9e020892e8e2ede5847f5"},
{file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bc66318fb7ee012071b2792024564973ecc80e9522842eb4e17743604b5e045"},
{file = "pyrsistent-0.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:914474c9f1d93080338ace89cb2acee74f4f666fb0424896fcfb8d86058bf17c"},
{file = "pyrsistent-0.18.1-cp39-cp39-win32.whl", hash = "sha256:1b34eedd6812bf4d33814fca1b66005805d3640ce53140ab8bbb1e2651b0d9bc"},
{file = "pyrsistent-0.18.1-cp39-cp39-win_amd64.whl", hash = "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07"},
{file = "pyrsistent-0.18.1.tar.gz", hash = "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96"},
]
requests = [
{file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"},
{file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"},
]
"ruamel.yaml" = [
{file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"},
{file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"},
]
"ruamel.yaml.clib" = [
{file = "ruamel.yaml.clib-0.2.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0"},
{file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7"},
{file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win32.whl", hash = "sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee"},
{file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de"},
{file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751"},
{file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527"},
{file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win32.whl", hash = "sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5"},
{file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win_amd64.whl", hash = "sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c"},
{file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502"},
{file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78"},
{file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win32.whl", hash = "sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94"},
{file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win_amd64.whl", hash = "sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468"},
{file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd"},
{file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99"},
{file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win32.whl", hash = "sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb"},
{file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win_amd64.whl", hash = "sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe"},
{file = "ruamel.yaml.clib-0.2.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233"},
{file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84"},
{file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win32.whl", hash = "sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b"},
{file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win_amd64.whl", hash = "sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277"},
{file = "ruamel.yaml.clib-0.2.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed"},
{file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0"},
{file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win32.whl", hash = "sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104"},
{file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win_amd64.whl", hash = "sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7"},
{file = "ruamel.yaml.clib-0.2.6.tar.gz", hash = "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd"},
]
semgrep = [
{file = "semgrep-0.82.0-cp37.cp38.cp39.py37.py38.py39-none-any.whl", hash = "sha256:1750f2b755ffc7db4690e6c5990e03e03729e6de4d4c4edf3f39435020244989"},
{file = "semgrep-0.82.0-cp37.cp38.cp39.py37.py38.py39-none-macosx_10_14_x86_64.whl", hash = "sha256:037124db822a0c9235ea6cfbc637f06771ca2882d181dfdbd460c430bf51d7ec"},
{file = "semgrep-0.82.0.tar.gz", hash = "sha256:aa6c57d08602ffde3bf36159c0325d6d72f70dc634cd237f30ff46f7d6b78be3"},
]
tqdm = [
{file = "tqdm-4.62.3-py2.py3-none-any.whl", hash = "sha256:8dd278a422499cd6b727e6ae4061c40b48fce8b76d1ccbf5d34fca9b7f925b0c"},
{file = "tqdm-4.62.3.tar.gz", hash = "sha256:d359de7217506c9851b7869f3708d8ee53ed70a1b8edbba4dbcb47442592920d"},
]
typing-extensions = [
{file = "typing_extensions-4.1.0-py3-none-any.whl", hash = "sha256:c13180fbaa7cd97065a4915ceba012bdb31dc34743e63ddee16360161d358414"},
{file = "typing_extensions-4.1.0.tar.gz", hash = "sha256:ba97c5143e5bb067b57793c726dd857b1671d4b02ced273ca0538e71ff009095"},
]
urllib3 = [
{file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"},
{file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
]
wcmatch = [
{file = "wcmatch-8.3-py3-none-any.whl", hash = "sha256:7141d2c85314253f16b38cb3d6cc0fb612918d407e1df3ccc2be7c86cc259c22"},
{file = "wcmatch-8.3.tar.gz", hash = "sha256:371072912398af61d1e4e78609e18801c6faecd3cb36c54c82556a60abc965db"},
]
zipp = [
{file = "zipp-3.7.0-py3-none-any.whl", hash = "sha256:b47250dd24f92b7dd6a0a8fc5244da14608f3ca90a5efcd37a3b1642fac9a375"},
{file = "zipp-3.7.0.tar.gz", hash = "sha256:9f50f446828eb9d45b267433fd3e9da8d801f614129124863f9c51ebceafb87d"},
]

11
pyproject.toml Normal file
View file

@ -0,0 +1,11 @@
[tool.poetry]
name = "pushbits"
version = "0.0.0"
description = ""
authors = []
[tool.poetry.dependencies]
python = "^3.7"
[tool.poetry.dev-dependencies]
semgrep = "^0.82.0"

1
tests/semgrep-rules Submodule

@ -0,0 +1 @@
Subproject commit 46e040f844c2b1c40599c8d4f1cdf277b197329e