Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.pyc
*.swp
build
urlquery/apikey.py
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ returned is determined by the key and its associated permissions.


Note: The access of the default key (no key) is very limited, even more than
whatis accessible on the public site.
what is accessible on the public site.

API calls
=========
Expand Down Expand Up @@ -97,16 +97,20 @@ of the API call. This is called "_response_"
API Key
=======

If you have an API Key, put it in apikey.py with the variable name 'key'.
If you have an API Key, put it in a config file, and pass it into the API methods via the `apikey` parameter.

Gzip
====

To get the responses of the api gzip'ed, change 'gzip_default' to True.
To get the responses of the api gzip'ed, set the `gzip` parameter to `True`.

Dependencies
============

If `pip` is available, any prerequisites may be added via the usual "requirements" dance:

`pip install -r requirements.txt`

Hard:

* requests: https://github.com/kennethreitz/Requests
Expand Down
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
requests==2.3.0
dateutils==0.6.6
1 change: 1 addition & 0 deletions urlquery/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from api import *
from ooapi import URLQuery
123 changes: 78 additions & 45 deletions urlquery/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,51 @@

try:
import simplejson as json
except:
except ImportError:
import json

import requests
from dateutil.parser import parse
from datetime import datetime, timedelta
import time
try:
from apikey import key
except:
key = ''


base_url = 'https://uqapi.net/v3/json'
gzip_default = False

__feed_type = ['unfiltered', 'flagged']
__intervals = ['hour', 'day']
__priorities = ['urlfeed', 'low', 'medium', 'high']
__search_types = ['string', 'regexp', 'ids_alert', 'urlquery_alert', 'js_script_hash']
__search_types = ['string', 'regexp', 'ids_alert',
'urlquery_alert', 'js_script_hash']
__result_types = ['reports', 'url_list']
__url_matchings = ['url_host', 'url_path']


def __set_default_values(gzip=False):
def __set_default_values(gzip=False, apikey=None):
to_return = {}
to_return['key'] = key

if apikey is not None:
to_return['key'] = apikey
else:
to_return['key'] = ''

if gzip_default or gzip:
to_return['gzip'] = True

return to_return

def __query(query, gzip=False):

def __query(query, gzip=False, apikey=None):
if query.get('error') is not None:
return query
query.update(__set_default_values(gzip))
query.update(__set_default_values(gzip, apikey))
r = requests.post(base_url, data=json.dumps(query))
return r.json()

def urlfeed(feed='unfiltered', interval='hour', timestamp=None):

def urlfeed(feed='unfiltered', interval='hour', timestamp=None,
gzip=False, apikey=None):
"""
The urlfeed function is used to access the main feed of URL from
the service. Currently there are two distinct feed:
Expand Down Expand Up @@ -71,17 +78,19 @@ def urlfeed(feed='unfiltered', interval='hour', timestamp=None):
:return: URLFEED

{
"start_time" : string,
"end_time" : string,
"feed" : [URLs] Array of URL objects (see README)
"start_time" : string,
"end_time" : string,
"feed" : [URLs] Array of URL objects (see README)
}

"""
query = {'method': 'urlfeed'}
if feed not in __feed_type:
query.update({'error': 'Feed can only be in ' + ', '.join(__feed_type)})
query.update({'error':
'Feed can only be in ' + ', '.join(__feed_type)})
if interval not in __intervals:
query.update({'error': 'Interval can only be in ' + ', '.join(__intervals)})
query.update({'error':
'Interval can only be in ' + ', '.join(__intervals)})
if timestamp is None:
ts = datetime.now()
if interval == 'hour':
Expand All @@ -93,15 +102,17 @@ def urlfeed(feed='unfiltered', interval='hour', timestamp=None):
try:
timestamp = time.mktime(parse(timestamp).utctimetuple())
except:
query.update({'error': 'Unable to convert time to timestamp: ' + str(time)})
query.update({'error':
'Unable to convert time to timestamp: ' + str(time)})
query['feed'] = feed
query['interval'] = interval
query['timestamp'] = timestamp
return __query(query)
return __query(query, gzip, apikey)


def submit(url, useragent=None, referer=None, priority='low',
access_level='public', callback_url=None, submit_vt=False,
save_only_alerted=False):
access_level='public', callback_url=None, submit_vt=False,
save_only_alerted=False, gzip=False, apikey=None):
"""
Submits an URL for analysis.

Expand All @@ -124,7 +135,8 @@ def submit(url, useragent=None, referer=None, priority='low',

:param access_level: Set accessibility of the report
* *public*: URL is publicly available on the site (default)
* *nonpublic*: Shared with other security organizations/researchers.
* *nonpublic*: Shared with other security organizations or
researchers.
* *private*: Only submitting key has access.

:param callback_url: Results are POSTed back to the provided
Expand Down Expand Up @@ -167,9 +179,12 @@ def submit(url, useragent=None, referer=None, priority='low',
"""
query = {'method': 'submit'}
if priority not in __priorities:
query.update({'error': 'priority must be in '+', '.join(__priorities)})
query.update({'error':
'priority must be in ' + ', '.join(__priorities)})
if access_level not in __access_levels:
query.update({'error': 'assess_level must be in '+', '.join(__access_levels)})
query.update({'error':
'assess_level must be in '
+ ', '.join(__access_levels)})
query['url'] = url
if useragent is not None:
query['useragent'] = useragent
Expand All @@ -183,20 +198,23 @@ def submit(url, useragent=None, referer=None, priority='low',
query['submit_vt'] = True
if save_only_alerted:
query['save_only_alerted'] = True
return __query(query)
return __query(query, gzip, apikey)


def user_agent_list():
def user_agent_list(gzip=False, apikey=None):
"""
Returns a list of accepted user agent strings. These might
change over time, select one from the returned list.

:return: A list of accepted user agents
"""
query = {'method': 'user_agent_list'}
return __query(query)
return __query(query, gzip, apikey)


def mass_submit(urls, useragent=None, referer=None,
access_level='public', priority='low', callback_url=None):
access_level='public', priority='low', callback_url=None,
gzip=False, apikey=None):
"""
See submit for details. All URLs will be queued with the same settings.

Expand All @@ -208,7 +226,8 @@ def mass_submit(urls, useragent=None, referer=None,
"""
query = {'method': 'mass_submit'}
if access_level not in __access_levels:
query.update({'error': 'assess_level must be in '+', '.join(__access_levels)})
query.update({'error':
'assess_level must be in '+', '.join(__access_levels)})
if priority not in __priorities:
query.update({'error': 'priority must be in '+', '.join(__priorities)})
if useragent is not None:
Expand All @@ -219,9 +238,10 @@ def mass_submit(urls, useragent=None, referer=None,
query['priority'] = priority
if callback_url is not None:
query['callback_url'] = callback_url
return __query(query)
return __query(query, gzip, apikey)


def queue_status(queue_id):
def queue_status(queue_id, gzip=False, apikey=None):
"""
Polls the current status of a queued URL. Normal processing time
for a URL is about 1 minute.
Expand All @@ -232,11 +252,12 @@ def queue_status(queue_id):
"""
query = {'method': 'queue_status'}
query['queue_id'] = queue_id
return __query(query)
return __query(query, gzip=False, apikey=None)


def report(report_id, recent_limit=0, include_details=False,
include_screenshot=False, include_domain_graph=False):
include_screenshot=False, include_domain_graph=False,
gzip=False, apikey=None):
"""
This extracts data for a given report, the amount of data and
what is included is dependent on the parameters set and the
Expand Down Expand Up @@ -288,9 +309,10 @@ def report(report_id, recent_limit=0, include_details=False,
query['include_screenshot'] = True
if include_domain_graph:
query['include_domain_graph'] = True
return __query(query)
return __query(query, gzip, apikey)

def report_list(timestamp=None, limit=50):

def report_list(timestamp=None, limit=50, gzip=False, apikey=None):
"""
Returns a list of reports created from the given timestamp, if it’s
not included the most recent reports will be returned.
Expand Down Expand Up @@ -324,13 +346,16 @@ def report_list(timestamp=None, limit=50):
try:
timestamp = time.mktime(parse(timestamp).utctimetuple())
except:
query.update({'error': 'Unable to convert time to timestamp: ' + str(time)})
query.update({'error':
'Unable to convert time to timestamp: ' + str(time)})
query['timestamp'] = timestamp
query['limit'] = limit
return __query(query)
return __query(query, gzip, apikey)


def search(q, search_type='string', result_type='reports',
url_matching='url_host', date_from=None, deep=False):
url_matching='url_host', date_from=None, deep=False,
gzip=False, apikey=None):
"""
Search in the database

Expand Down Expand Up @@ -370,11 +395,17 @@ def search(q, search_type='string', result_type='reports',
"""
query = {'method': 'search'}
if search_type not in __search_types:
query.update({'error': 'search_type can only be in ' + ', '.join(__search_types)})
query.update({'error':
'search_type can only be in '
+ ', '.join(__search_types)})
if result_type not in __result_types:
query.update({'error': 'result_type can only be in ' + ', '.join(__result_types)})
query.update({'error':
'result_type can only be in '
+ ', '.join(__result_types)})
if url_matching not in __url_matchings:
query.update({'error': 'url_matching can only be in ' + ', '.join(__url_matchings)})
query.update({'error':
'url_matching can only be in '
+ ', '.join(__url_matchings)})

if date_from is None:
ts = datetime.now()
Expand All @@ -383,7 +414,9 @@ def search(q, search_type='string', result_type='reports',
try:
timestamp = time.mktime(parse(date_from).utctimetuple())
except:
query.update({'error': 'Unable to convert time to timestamp: ' + str(time)})
query.update({'error':
'Unable to convert time to timestamp: '
+ str(time)})

query['q'] = q
query['search_type'] = search_type
Expand All @@ -392,9 +425,10 @@ def search(q, search_type='string', result_type='reports',
query['from'] = timestamp
if deep:
query['deep'] = True
return __query(query)
return __query(query, gzip, apikey)

def reputation(q):

def reputation(q, gzip=False, apikey=None):
"""
Searches a reputation list of URLs detected over the last month.
The search query can be a domain or an IP.
Expand All @@ -407,5 +441,4 @@ def reputation(q):

query = {'method': 'reputation'}
query['q'] = q
return __query(query)

return __query(query, gzip, apikey)
Loading