Skip to content

Commit

Permalink
Merge pull request #7 from spark1security/ms/wrike
Browse files Browse the repository at this point in the history
Added support to Wrike.
  • Loading branch information
blupants authored Dec 4, 2023
2 parents 9046e15 + 830c855 commit 7922382
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 6 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@


# n0s1 - Secret Scanner
n0s1 ([pronunciation](https://en.wiktionary.org/wiki/nosy#Pronunciation)) is a secret scanner for Jira, Confluence, Asana and Linear.app. It scans all tickets/items/issues within the chosen platform in search of any leaked secrets in the titles, bodies, and comments. It is open-source and it can be easily extended to support scanning many others Project Management and Issue Tracker platforms.
n0s1 ([pronunciation](https://en.wiktionary.org/wiki/nosy#Pronunciation)) is a secret scanner for Jira, Confluence, Asana, Wrike and Linear.app. It scans all tickets/items/issues within the chosen platform in search of any leaked secrets in the titles, bodies, and comments. It is open-source and it can be easily extended to support scanning many others Project Management and Issue Tracker platforms.

These secrets are identified by comparing them against an adaptable configuration file named [regex.yaml](https://github.com/spark1security/n0s1/blob/main/src/n0s1/config/regex.yaml). Alternative TOML format is also supported: [regex.toml](https://github.com/spark1security/n0s1/blob/main/src/n0s1/config/regex.toml). The scanner specifically looks for sensitive information, which includes:
* Github Personal Access Tokens
Expand All @@ -26,6 +26,7 @@ These secrets are identified by comparing them against an adaptable configuratio
* [Jira](https://www.atlassian.com/software/jira)
* [Confluence](https://www.atlassian.com/software/confluence)
* [Asana](https://asana.com)
* [Wrike](https://www.wrike.com)
* [Linear](https://linear.app/)

### Quick Start
Expand Down
4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ toml
jira
pyyaml
atlassian-python-api
asana
asana
WrikePy
BeautifulSoup4
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def get_version():
setup(
name="n0s1",
version=get_version(),
description="Secret Scanner for Jira, Confluence, Asana and Linear. Prevent credential leaks with n0s1.",
description="Secret Scanner for Jira, Confluence, Asana, Wrike and Linear. Prevent credential leaks with n0s1.",
long_description=long_description,
long_description_content_type="text/markdown",
url="https://spark1.us/n0s1",
Expand All @@ -47,7 +47,7 @@ def get_version():
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
], # Classifiers help users find your project by categorizing it https://pypi.org/classifiers/
keywords="security, cybersecurity, scanner, secret scanner, secret leak, data leak, Jira, Confluence, Asana, Linear, security scanner",
keywords="security, cybersecurity, scanner, secret scanner, secret leak, data leak, Jira, Confluence, Asana, Wrike, Linear, security scanner",
package_dir={"": "src"},
packages=find_packages(where="src"),
python_requires=">=3.8, <4",
Expand Down
2 changes: 1 addition & 1 deletion src/n0s1/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.0.14"
__version__ = "1.0.15"
4 changes: 4 additions & 0 deletions src/n0s1/controllers/platform_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,13 @@ def get_platform(self, platform):
from . import confluence_controller as confluence_controller
from . import linear_controller as linear_controller
from . import asana_controller as asana_controller
from . import wrike_controller as wrike_controller
except Exception:
import n0s1.controllers.jira_controller as jira_controller
import n0s1.controllers.confluence_controller as confluence_controller
import n0s1.controllers.linear_controller as linear_controller
import n0s1.controllers.asana_controller as asana_controller
import n0s1.controllers.wrike_controller as wrike_controller

factory.register_platform("", jira_controller.JiraControler)
factory.register_platform("jira", jira_controller.JiraControler)
Expand All @@ -54,3 +56,5 @@ def get_platform(self, platform):
factory.register_platform("linear_scan", linear_controller.LinearControler)
factory.register_platform("asana", asana_controller.AsanaControler)
factory.register_platform("asana_scan", asana_controller.AsanaControler)
factory.register_platform("wrike", wrike_controller.WrikeControler)
factory.register_platform("wrike_scan", wrike_controller.WrikeControler)
105 changes: 105 additions & 0 deletions src/n0s1/controllers/wrike_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import json
import logging
from WrikePy import *


class WrikeControler():
def __init__(self):
self._client = None

def set_config(self, config):
TOKEN = config.get("token", "")
base_url = "https://www.wrike.com/api/v4"
self._client = Wrike(base_url, TOKEN)
return self.is_connected()

def get_name(self):
return "Wrike"

def is_connected(self):
if self._client:
acc = Account(self._client)
response = acc.query__account()
logged_in = False
try:
if 200 <= response.status_code < 300:
acc_info = json.loads(response.text)
logged_in = True
logging.info(f"Logged to {self.get_name()} as {acc_info}")
except Exception as ex:
logging.warning(ex)
pass
if not logged_in:
logging.error(f"Unable to connect to {self.get_name()} instance. Check your credentials.")
return False

t = Tasks(self._client, parameters={"fields": ["description"]})
response = t.query__tasks()
list_tasks = False

try:
if 200 <= response.status_code < 300:
tasks = json.loads(response.text)
task_list = tasks.get("data", [])
if len(task_list) > 0:
list_tasks = True
return True
except Exception as ex:
logging.warning(ex)
pass
if not list_tasks:
logging.error(f"Unable to list {self.get_name()} tasks. Check your permissions.")
return False

def get_data(self, include_coments=False):
if not self._client:
return None, None, None, None, None

t = Tasks(self._client, parameters={"fields": ["description"]})
response = t.query__tasks()
tasks = {}
try:
if 200 <= response.status_code < 300:
tasks = json.loads(response.text)
except Exception as ex:
logging.warning(ex)

task_list = tasks.get("data", [])
for t in task_list:
title = t.get("title", "")
description = t.get("description", "")
url = t.get("permalink", "")
comments = []
if task_id := t.get("id", None):
if include_coments:
comments_obj = Comments(self._client, [task_id])
response = comments_obj.query__tasks_taskId_comments()
json_data = {}
try:
if 200 <= response.status_code < 300:
json_data = json.loads(response.text)
except Exception as ex:
logging.warning(ex)

c_data = json_data.get("data", [])
for c in c_data:
comments.append(c.get("text", ""))
yield title, description, comments, url, task_id

def post_comment(self, task_id, comment):
if not self._client:
return False
comment = comment.replace("<REDACTED>", "**********")
comment = comment.replace("\n", "<br>")
comments_obj = Comments(self._client, [task_id], parameters={"text": comment, "plainText": False})
if comments_obj:
response = comments_obj.create__tasks_taskId_comments()
try:
if 200 <= response.status_code < 300:
json_data = json.loads(response.text)
added_comment = json_data.get("data", [])[0]
if len(added_comment) > 0:
return True
except Exception as ex:
logging.warning(ex)
return False
19 changes: 18 additions & 1 deletion src/n0s1/n0s1.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def init_argparse() -> argparse.ArgumentParser:
install_path = os.path.dirname(os.path.abspath(__file__))
parser = argparse.ArgumentParser(
prog="n0s1",
description="""Secret scanner for Project Management platforms such as Jira, Confluence, Asana and Linear.
description="""Secret scanner for Project Management platforms such as Jira, Confluence, Asana, Wrike and Linear.
""",
)

Expand Down Expand Up @@ -147,6 +147,17 @@ def init_argparse() -> argparse.ArgumentParser:
help="Asana API key. Ref: https://developers.asana.com/docs/personal-access-token#generating-a-pat"
)

wrike_scan_parser = subparsers.add_parser(
"wrike_scan", help="Scan Wrike tasks", parents=[parent_parser]
)
wrike_scan_parser.add_argument(
"--api-key",
dest="api_key",
nargs="?",
type=str,
help="Wrike permanent token. Ref: https://help.wrike.com/hc/en-us/articles/210409445-Wrike-API#UUID-a1b0051a-0537-2215-c542-3b04d7205f4b_section-idm232163770698441"
)

linear_scan_parser = subparsers.add_parser(
"linear_scan", help="Scan Linear issues", parents=[parent_parser]
)
Expand Down Expand Up @@ -449,6 +460,12 @@ def main():
TOKEN = args.api_key
controler_config = {"token": TOKEN}

elif command == "wrike_scan":
TOKEN = os.getenv("WRIKE_TOKEN")
if args.api_key and len(args.api_key) > 0:
TOKEN = args.api_key
controler_config = {"token": TOKEN}

elif command == "jira_scan":
SERVER = os.getenv("JIRA_SERVER")
EMAIL = os.getenv("JIRA_EMAIL")
Expand Down

0 comments on commit 7922382

Please sign in to comment.