Skip to content

Commit 91af63c

Browse files
author
Hifumi1337
authored
Merge pull request #119 from InQuest/develop
v1.0.0-beta9
2 parents c677eb6 + 1e90c45 commit 91af63c

11 files changed

Lines changed: 210 additions & 37 deletions

File tree

.github/workflows/workflow.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: threatingestor-workflow
2+
3+
on: [push]
4+
5+
jobs:
6+
build:
7+
runs-on: ubuntu-latest
8+
strategy:
9+
matrix:
10+
python-version: ["3.6", "3.7"]
11+
12+
steps:
13+
- uses: actions/checkout@v3
14+
- name: Set up Python ${{ matrix.python-version }}
15+
uses: actions/setup-python@v4
16+
with:
17+
python-version: ${{ matrix.python-version }}
18+
- name: Install dependencies
19+
run: |
20+
pip install -r requirements.txt
21+
pip install -r requirements-testing.txt
22+
- name: Test scripts
23+
run: nosetests --with-coverage --cover-package=threatingestor --cover-xml

Dockerfile

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
# Comes with Python 3.6.9 installed by default
2+
FROM ubuntu:18.04
3+
4+
RUN apt-get update
5+
RUN apt-get install python3-pip -y
6+
RUN apt-get install sqlite3
7+
8+
RUN pip3 install threatingestor \
9+
twitter \
10+
feedparser
11+
COPY config.yml .
12+
13+
# Run the ThreatIngestor without accessing /bin/bash container
14+
CMD ["threatingestor", "config.yml"]

README.rst

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,22 @@ ThreatIngestor
66
:alt: Developed by InQuest
77
.. image:: https://travis-ci.org/InQuest/ThreatIngestor.svg?branch=master
88
:target: https://travis-ci.org/InQuest/ThreatIngestor
9-
:alt: Build Status
9+
:alt: Build Status (Travis CI)
10+
11+
.. Change ?branch=develop to ?branch=master when merging into master
12+
.. image:: https://github.com/InQuest/ThreatIngestor/workflows/threatingestor-workflow/badge.svg?branch=develop
13+
:target: https://github.com/InQuest/ThreatIngestor/actions
14+
:alt: Build Status (GitHub Workflow)
15+
1016
.. image:: https://readthedocs.org/projects/threatingestor/badge/?version=latest
1117
:target: http://inquest.readthedocs.io/projects/threatingestor/en/latest/?badge=latest
1218
:alt: Documentation Status
13-
.. image:: https://api.codacy.com/project/badge/Grade/a989bb12e9604d5a9577ce71848e7a2a
14-
:target: https://app.codacy.com/app/InQuest/ThreatIngestor
15-
:alt: Code Health
16-
.. image:: https://api.codacy.com/project/badge/Coverage/a989bb12e9604d5a9577ce71848e7a2a
17-
:target: https://app.codacy.com/app/InQuest/ThreatIngestor
18-
:alt: Test Coverage
19+
.. .. image:: https://api.codacy.com/project/badge/Grade/a989bb12e9604d5a9577ce71848e7a2a
20+
.. :target: https://app.codacy.com/app/InQuest/ThreatIngestor
21+
.. :alt: Code Health
22+
.. .. image:: https://api.codacy.com/project/badge/Coverage/a989bb12e9604d5a9577ce71848e7a2a
23+
.. :target: https://app.codacy.com/app/InQuest/ThreatIngestor
24+
.. :alt: Test Coverage
1925
.. image:: http://img.shields.io/pypi/v/ThreatIngestor.svg
2026
:target: https://pypi.python.org/pypi/ThreatIngestor
2127
:alt: PyPi Version
@@ -74,6 +80,7 @@ Sources
7480
* `Beanstalk work queues <https://inquest.readthedocs.io/projects/threatingestor/en/latest/sources/beanstalk.html>`__
7581
* `Git repositories <https://inquest.readthedocs.io/projects/threatingestor/en/latest/sources/git.html>`__
7682
* `GitHub repository search <https://inquest.readthedocs.io/projects/threatingestor/en/latest/sources/github.html>`__
83+
* `Gists by username <https://inquest.readthedocs.io/projects/threatingestor/en/latest/sources/github_gist.html>`__
7784
* `RSS feeds <https://inquest.readthedocs.io/projects/threatingestor/en/latest/sources/rss.html>`__
7885
* `Amazon SQS queues <https://inquest.readthedocs.io/projects/threatingestor/en/latest/sources/sqs.html>`__
7986
* `Twitter <https://inquest.readthedocs.io/projects/threatingestor/en/latest/sources/twitter.html>`__
@@ -127,3 +134,20 @@ Issues and pull requests are welcomed. Please keep Python code PEP8 compliant. B
127134
.. _ThreatIngestor walkthroughs: https://inquest.net/taxonomy/term/42
128135
.. _RSS config file: https://github.com/InQuest/ThreatIngestor/blob/master/rss.example.yml
129136
.. _labs.inquest.net/iocdb: https://labs.inquest.net/iocdb
137+
138+
Docker Container
139+
------------
140+
141+
A Dockerfile is now available for running ThreatIngestor within a Docker container.
142+
143+
First, you'll need to build the container::
144+
145+
docker build . -t threat
146+
147+
After that, you can mount the container for use using this command::
148+
149+
docker run -it --mount type=bind,source=/,target=/dock threat /bin/bash
150+
151+
After you've mounted the container, and you're inside of the `/bin/bash` shell, you can run the threatingestor like normal::
152+
153+
threatingestor config.yml

docs/api.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,15 @@ github
167167
:show-inheritance:
168168
:member-order: bysource
169169

170+
github_gist
171+
^^^^^^
172+
173+
.. automodule:: threatingestor.sources.github_gist
174+
:members:
175+
:undoc-members:
176+
:show-inheritance:
177+
:member-order: bysource
178+
170179
rss
171180
^^^
172181

docs/sources/github.rst

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ Configuration Options
1010
~~~~~~~~~~~~~~~~~~~~~
1111

1212
* ``module`` (required): ``github``
13-
* ``search`` (required): search term(s).
14-
* ``username``: Optional username for authentication.
15-
* ``token``: Optional token or password for authentication.
13+
* ``search`` (required): Search term(s).
14+
* ``username`` (optional): Username for authentication.
15+
* ``token`` (optional): Token or password for authentication.
16+
* ``num_of_days`` (optional): Search within a specific number of days since repository creation date.
1617

1718
Example Configuration
1819
~~~~~~~~~~~~~~~~~~~~~
@@ -41,5 +42,6 @@ Inside the ``sources`` section of your configuration file:
4142
credentials: github-auth
4243
module: github
4344
search: CVE-2018-
45+
num_of_days: 60
4446
4547
.. _repository search API: https://developer.github.com/v3/search/#search-repositories

docs/sources/github_gist.rst

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
.. _github-gist-source:
2+
3+
GitHub Gist Username Search
4+
------------------------
5+
6+
The **GitHub Gist** source plugin uses GitHub's `gist API`_ to find new gists created by a user, and create a :ref:`Task artifact <task-artifact>` for each.
7+
8+
Configuration Options
9+
~~~~~~~~~~~~~~~~~~~~~
10+
11+
* ``module`` (required): ``github_gist``
12+
* ``user`` (required): Username of the gist owner.
13+
* ``username`` (optional): Username for authentication.
14+
* ``token`` (optional): Token or password for authentication.
15+
16+
Example Configuration
17+
~~~~~~~~~~~~~~~~~~~~~
18+
19+
The following examples all assume GitHub credentials have already been
20+
configured in the ``credentials`` section of the config, like this:
21+
22+
.. code-block:: yaml
23+
24+
credentials:
25+
- name: github-auth
26+
username: myuser
27+
token: MYTOKEN
28+
29+
.. note::
30+
31+
GitHub credentials are optional, but increase the rate limit for API
32+
requests *significantly*. If you are doing more than one or two low-
33+
volume searches, you should set up the credentials.
34+
35+
Inside the ``sources`` section of your configuration file:
36+
37+
.. code-block:: yaml
38+
39+
- name: github-gist-search
40+
credentials: github-auth
41+
module: github_gist
42+
user: InQuest
43+
44+
.. _github gist user API: https://docs.github.com/en/rest/gists/gists#list-gists-for-a-user

docs/workflows.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,11 @@ And the ThreatIngestor config file:
394394
credentials: github-auth
395395
search: CVE-2018-
396396
397+
- name: github-gist-search
398+
module: github_gist
399+
credentials: github-auth
400+
user: InQuest
401+
397402
- name: git-yara-rules
398403
module: git
399404
url: https://github.com/InQuest/yara-rules.git

tests/test_sources_github.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
import threatingestor.sources.github
88

9-
109
API_RESPONSE_DATA = """
1110
{
1211
"total_count": 40,
@@ -57,7 +56,7 @@ def setUp(self):
5756
@patch('threatingestor.sources.github.datetime')
5857
@responses.activate
5958
def test_run_returns_saved_state_tasks(self, mock_datetime):
60-
responses.add(responses.GET, threatingestor.sources.github.SEARCH_URL,
59+
responses.add(responses.GET, threatingestor.sources.github.REPO_SEARCH_URL,
6160
body=API_RESPONSE_DATA)
6261
mock_datetime.datetime.utcnow.return_value = datetime.datetime(2018, 4, 30, 17, 5, 13, 194840)
6362
mock_datetime.datetime.side_effect = lambda *args, **kw: datetime.datetime(*args, **kw)

threatingestor/sources/github.py

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,30 @@
1-
import datetime
2-
3-
4-
import requests
5-
1+
import datetime, requests
62

73
from threatingestor.sources import Source
84
import threatingestor.artifacts
95

10-
11-
SEARCH_URL = "https://api.github.com/search/repositories"
12-
6+
REPO_SEARCH_URL = "https://api.github.com/search/repositories"
137

148
class Plugin(Source):
159
"""Github Source Plugin"""
16-
def __init__(self, name, search, username="", token=""):
10+
def __init__(self, name, search, num_of_days=10, username="", token=""):
1711
self.name = name
1812
self.search = search
13+
self.num_of_days = num_of_days
1914

2015
if username and token:
2116
self.auth = (username, token)
2217
else:
2318
self.auth = None
2419

25-
2620
def _repository_search(self, params):
2721
"""Returns a list of repository results."""
22+
2823
# Iterates through pages of results from query.
29-
response = requests.get(SEARCH_URL, params=params, auth=self.auth)
24+
response = requests.get(REPO_SEARCH_URL, params=params, auth=self.auth)
3025

3126
repo_list = []
27+
3228
while True:
3329
for repo in response.json().get('items', []):
3430
repo_list.append(repo)
@@ -37,35 +33,33 @@ def _repository_search(self, params):
3733
break
3834

3935
response = requests.get(
40-
response.links.get('next')["url"],
41-
auth=self.auth)
36+
response.links.get('next')["url"], auth=self.auth)
4237

4338
return repo_list
4439

45-
4640
def run(self, saved_state):
4741
"""Returns a list of artifacts and the saved state"""
4842
# If no saved_state, search max 1 day ago.
4943
if not saved_state:
50-
saved_state = (datetime.datetime.utcnow() -
51-
datetime.timedelta(days=10)).isoformat()[:-7] + 'Z'
44+
saved_state = (datetime.datetime.utcnow() - datetime.timedelta(days=self.num_of_days)).isoformat()[:-7] + 'Z'
5245

5346
params = {
5447
'q': "{search} created:>={timestamp}".format(
5548
search=self.search,
5649
timestamp=saved_state),
57-
"per_page": "100"}
50+
"per_page": "100"
51+
}
5852

5953
saved_state = datetime.datetime.utcnow().isoformat()[:-7] + 'Z'
6054
repo_list = self._repository_search(params)
6155

6256
artifact_list = []
57+
6358
for repo in repo_list:
6459
title = "Manual Task: GitHub {u}".format(u=repo['full_name'])
6560
description = 'URL: {u}\nTask autogenerated by ThreatIngestor from source: {s}'
6661
description = description.format(s=self.name, u=repo['html_url'])
67-
artifact = threatingestor.artifacts.Task(
68-
title, self.name, reference_link=repo['html_url'], reference_text=description)
62+
artifact = threatingestor.artifacts.Task(title, self.name, reference_link=repo['html_url'], reference_text=description)
6963

7064
artifact_list.append(artifact)
7165

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import datetime, requests
2+
3+
from threatingestor.sources import Source
4+
import threatingestor.artifacts
5+
6+
def user_set(user):
7+
GIST_SEARCH_URL = "https://api.github.com/users/{0}/gists".format(user)
8+
return GIST_SEARCH_URL
9+
10+
class Plugin(Source):
11+
"""Github Gist Source Plugin"""
12+
def __init__(self, name, user="", username="", token=""):
13+
self.name = name
14+
self.user = user
15+
16+
if username and token:
17+
self.auth = (username, token)
18+
else:
19+
self.auth = None
20+
21+
def _gist_search(self, params):
22+
"""Returns a list of gist results."""
23+
24+
# Iterates through pages of results from query.
25+
response = requests.get(user_set(self.user), params=params, auth=self.auth)
26+
27+
gist_list = []
28+
29+
for gist in response.json():
30+
gist_list.append(gist)
31+
32+
return gist_list
33+
34+
def run(self, saved_state):
35+
"""Returns a list of artifacts and the saved state"""
36+
37+
params = { "per_page": "100" }
38+
39+
saved_state = datetime.datetime.utcnow().isoformat()[:-7] + 'Z'
40+
gist_list = self._gist_search(params)
41+
42+
artifact_list = []
43+
44+
for gist in gist_list:
45+
title = "Gist Owner: {0}".format(self.user)
46+
description = 'URL: {u}\nTask autogenerated by ThreatIngestor from source: {s}'
47+
description = description.format(s=self.name, u=gist['html_url'])
48+
artifact = threatingestor.artifacts.Task(title, self.name, reference_link=gist['html_url'], reference_text=description)
49+
50+
artifact_list.append(artifact)
51+
52+
return saved_state, artifact_list

0 commit comments

Comments
 (0)