new: Support for TZ, Geo, locale, color scheme

pull/714/head
Raphaël Vinot 2023-06-07 15:05:40 +02:00
parent 5a61bdf5ee
commit 4ff9b7651d
5 changed files with 137 additions and 17 deletions

View File

@ -605,6 +605,10 @@ class Lookyloo():
http_credentials=query.get('http_credentials', None), http_credentials=query.get('http_credentials', None),
viewport=query.get('viewport', None), viewport=query.get('viewport', None),
referer=query.get('referer', None), referer=query.get('referer', None),
timezone_id=query.get('timezone_id', None),
locale=query.get('locale', None),
geolocation=query.get('geolocation', None),
color_scheme=query.get('color_scheme', None),
rendered_hostname_only=query.get('rendered_hostname_only', True), rendered_hostname_only=query.get('rendered_hostname_only', True),
# force=query.get('force', False), # force=query.get('force', False),
# recapture_interval=query.get('recapture_interval', 300), # recapture_interval=query.get('recapture_interval', 300),

41
poetry.lock generated
View File

@ -1238,18 +1238,18 @@ format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-
[[package]] [[package]]
name = "lacuscore" name = "lacuscore"
version = "1.5.2" version = "1.5.4"
description = "Core of Lacus, usable as a module" description = "Core of Lacus, usable as a module"
optional = false optional = false
python-versions = ">=3.8,<4.0" python-versions = ">=3.8,<4.0"
files = [ files = [
{file = "lacuscore-1.5.2-py3-none-any.whl", hash = "sha256:0ba3b4d99ced63824ab3274512dfd17f10f92b8f5eebf280bb779c81796e8967"}, {file = "lacuscore-1.5.4-py3-none-any.whl", hash = "sha256:bb118ee6e7603ecb18191e5eb60a74d2a9c700076d1c80ec581acefceb66def7"},
{file = "lacuscore-1.5.2.tar.gz", hash = "sha256:2c5d3a7c52130d934ecb3ae6b51d46e767f1959e56f5e71cd5ff573f2b1f887d"}, {file = "lacuscore-1.5.4.tar.gz", hash = "sha256:6de799e9735a32950445225db9018e1f52d593c29e1f536059b541ac7d6fb47d"},
] ]
[package.dependencies] [package.dependencies]
defang = ">=0.5.3,<0.6.0" defang = ">=0.5.3,<0.6.0"
playwrightcapture = {version = ">=1.20.2,<2.0.0", extras = ["recaptcha"]} playwrightcapture = {version = ">=1.20.3,<2.0.0", extras = ["recaptcha"]}
redis = {version = ">=4.5.5,<5.0.0", extras = ["hiredis"]} redis = {version = ">=4.5.5,<5.0.0", extras = ["hiredis"]}
requests = ">=2.31.0,<3.0.0" requests = ">=2.31.0,<3.0.0"
ua-parser = ">=0.16.1,<0.17.0" ua-parser = ">=0.16.1,<0.17.0"
@ -1876,13 +1876,13 @@ typing-extensions = {version = "*", markers = "python_version <= \"3.8\""}
[[package]] [[package]]
name = "playwrightcapture" name = "playwrightcapture"
version = "1.20.2" version = "1.20.4"
description = "A simple library to capture websites using playwright" description = "A simple library to capture websites using playwright"
optional = false optional = false
python-versions = ">=3.8,<4.0" python-versions = ">=3.8,<4.0"
files = [ files = [
{file = "playwrightcapture-1.20.2-py3-none-any.whl", hash = "sha256:49f1bba4df565a2e00489343b097bd7d4a4c62f1d6f7735a8f7917c6d7584a6b"}, {file = "playwrightcapture-1.20.4-py3-none-any.whl", hash = "sha256:e943b5cc3700402cfe395ca61c42b7812bde9d9c1c30c71abf219e2f700ac83b"},
{file = "playwrightcapture-1.20.2.tar.gz", hash = "sha256:4b2eec6f511c45b5c24b4ba9eec2f34325f997b82cedd0c0b1ea708b87652343"}, {file = "playwrightcapture-1.20.4.tar.gz", hash = "sha256:ae8b6b680992b992290260fbac16a07bedd0d0e3259d8864cba70272481a2b0a"},
] ]
[package.dependencies] [package.dependencies]
@ -1891,8 +1891,10 @@ dateparser = ">=1.1.8,<2.0.0"
lxml = ">=4.9.2,<5.0.0" lxml = ">=4.9.2,<5.0.0"
playwright = ">=1.34.0,<2.0.0" playwright = ">=1.34.0,<2.0.0"
pydub = {version = ">=0.25.1,<0.26.0", optional = true, markers = "extra == \"recaptcha\""} pydub = {version = ">=0.25.1,<0.26.0", optional = true, markers = "extra == \"recaptcha\""}
pytz = {version = ">=2023.3,<2024.0", markers = "python_version < \"3.9\""}
requests = {version = ">=2.31.0,<3.0.0", optional = true, markers = "extra == \"recaptcha\""} requests = {version = ">=2.31.0,<3.0.0", optional = true, markers = "extra == \"recaptcha\""}
SpeechRecognition = {version = ">=3.10.0,<4.0.0", optional = true, markers = "extra == \"recaptcha\""} SpeechRecognition = {version = ">=3.10.0,<4.0.0", optional = true, markers = "extra == \"recaptcha\""}
tzdata = ">=2023.3,<2024.0"
w3lib = ">=2.1.1,<3.0.0" w3lib = ">=2.1.1,<3.0.0"
[package.extras] [package.extras]
@ -2086,13 +2088,13 @@ docs = ["Sphinx (>=5.3.0,<6.0.0)"]
[[package]] [[package]]
name = "pylacus" name = "pylacus"
version = "1.5.0" version = "1.5.1"
description = "Python CLI and module for lacus" description = "Python CLI and module for lacus"
optional = false optional = false
python-versions = ">=3.8,<4.0" python-versions = ">=3.8,<4.0"
files = [ files = [
{file = "pylacus-1.5.0-py3-none-any.whl", hash = "sha256:178c21708a76bdef95c49a74fa84d6cb682793583d1e261eb767f06e081d8dd4"}, {file = "pylacus-1.5.1-py3-none-any.whl", hash = "sha256:abcbb2ea1d26f5807bfcb4d6cb3d973aba6a65b2024b2b30b83f5925a87c4196"},
{file = "pylacus-1.5.0.tar.gz", hash = "sha256:e7738ff708a6900a789061e3edb92d5ed9221c7630bc36aef402414f3b835b50"}, {file = "pylacus-1.5.1.tar.gz", hash = "sha256:4ab886e5acea4d50aa0a75db230797c5e0e91a937a598bbbe36a61db5bfd7f87"},
] ]
[package.dependencies] [package.dependencies]
@ -2690,6 +2692,17 @@ files = [
{file = "types_python_dateutil-2.8.19.13-py3-none-any.whl", hash = "sha256:0b0e7c68e7043b0354b26a1e0225cb1baea7abb1b324d02b50e2d08f1221043f"}, {file = "types_python_dateutil-2.8.19.13-py3-none-any.whl", hash = "sha256:0b0e7c68e7043b0354b26a1e0225cb1baea7abb1b324d02b50e2d08f1221043f"},
] ]
[[package]]
name = "types-pytz"
version = "2023.3.0.0"
description = "Typing stubs for pytz"
optional = false
python-versions = "*"
files = [
{file = "types-pytz-2023.3.0.0.tar.gz", hash = "sha256:ecdc70d543aaf3616a7e48631543a884f74205f284cefd6649ddf44c6a820aac"},
{file = "types_pytz-2023.3.0.0-py3-none-any.whl", hash = "sha256:4fc2a7fbbc315f0b6630e0b899fd6c743705abe1094d007b0e612d10da15e0f3"},
]
[[package]] [[package]]
name = "types-redis" name = "types-redis"
version = "4.5.5.2" version = "4.5.5.2"
@ -2783,13 +2796,13 @@ files = [
[[package]] [[package]]
name = "urllib3" name = "urllib3"
version = "2.0.2" version = "2.0.3"
description = "HTTP library with thread-safe connection pooling, file post, and more." description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false optional = false
python-versions = ">=3.7" python-versions = ">=3.7"
files = [ files = [
{file = "urllib3-2.0.2-py3-none-any.whl", hash = "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e"}, {file = "urllib3-2.0.3-py3-none-any.whl", hash = "sha256:48e7fafa40319d358848e1bc6809b208340fafe2096f1725d05d67443d0483d1"},
{file = "urllib3-2.0.2.tar.gz", hash = "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc"}, {file = "urllib3-2.0.3.tar.gz", hash = "sha256:bee28b5e56addb8226c96f7f13ac28cb4c301dd5ea8a6ca179c0b9835e032825"},
] ]
[package.extras] [package.extras]
@ -3060,4 +3073,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = ">=3.8,<3.12" python-versions = ">=3.8,<3.12"
content-hash = "0f6b7f573feddda1fe25d45e964e3b08d54e1f910ac4288f08c1a492994249ae" content-hash = "7042d65a2d0849ded20d663781db236b254b64b7ae13a7a323c0e358b7d57009"

View File

@ -65,14 +65,15 @@ passivetotal = "^2.5.9"
werkzeug = "^2.3.4" werkzeug = "^2.3.4"
filetype = "^1.2.0" filetype = "^1.2.0"
pypandora = "^1.4.0" pypandora = "^1.4.0"
lacuscore = "^1.5.2" lacuscore = "^1.5.4"
pylacus = "^1.5.0" pylacus = "^1.5.1"
pyipasnhistory = "^2.1.2" pyipasnhistory = "^2.1.2"
publicsuffixlist = "^0.10.0.20230506" publicsuffixlist = "^0.10.0.20230506"
pyfaup = "^1.2" pyfaup = "^1.2"
chardet = "^5.1.0" chardet = "^5.1.0"
pysecuritytxt = "^1.1.1" pysecuritytxt = "^1.1.1"
pylookyloomonitoring = "^1.0.5" pylookyloomonitoring = "^1.0.5"
pytz = {"version" = "^2023.3", python = "<3.9"}
[tool.poetry.group.dev.dependencies] [tool.poetry.group.dev.dependencies]
mypy = "^1.3.0" mypy = "^1.3.0"
@ -87,6 +88,7 @@ types-Deprecated = "^1.2.9.2"
types-python-dateutil = "^2.8.19.13" types-python-dateutil = "^2.8.19.13"
types-beautifulsoup4 = "^4.12.0.5" types-beautifulsoup4 = "^4.12.0.5"
types-Pillow = "^9.5.0.4" types-Pillow = "^9.5.0.4"
types-pytz = "^2023.3.0.0"
[build-system] [build-system]
requires = ["poetry_core"] requires = ["poetry_core"]

View File

@ -8,6 +8,7 @@ import json
import logging import logging
import logging.config import logging.config
import os import os
import sys
import time import time
import filetype # type: ignore import filetype # type: ignore
@ -15,7 +16,7 @@ import filetype # type: ignore
from datetime import date, datetime, timedelta, timezone from datetime import date, datetime, timedelta, timezone
from importlib.metadata import version from importlib.metadata import version
from io import BytesIO, StringIO from io import BytesIO, StringIO
from typing import Any, Dict, List, Optional, Union, TypedDict from typing import Any, Dict, List, Optional, Union, TypedDict, Set, Tuple
from urllib.parse import quote_plus, unquote_plus, urlparse from urllib.parse import quote_plus, unquote_plus, urlparse
from uuid import uuid4 from uuid import uuid4
from zipfile import ZipFile from zipfile import ZipFile
@ -35,6 +36,12 @@ from lookyloo.exceptions import MissingUUID, NoValidHarFile
from lookyloo.helpers import get_taxonomies, UserAgents, load_cookies from lookyloo.helpers import get_taxonomies, UserAgents, load_cookies
from lookyloo.lookyloo import Indexing, Lookyloo, CaptureSettings from lookyloo.lookyloo import Indexing, Lookyloo, CaptureSettings
if sys.version_info < (3, 9):
from pytz import all_timezones_set
else:
from zoneinfo import available_timezones
all_timezones_set = available_timezones()
from .genericapi import api as generic_api from .genericapi import api as generic_api
from .helpers import (User, build_users_table, get_secret_key, from .helpers import (User, build_users_table, get_secret_key,
load_user_from_request, src_request_ip, sri_load) load_user_from_request, src_request_ip, sri_load)
@ -200,6 +207,16 @@ def get_icon(icon_id: str) -> Optional[Icon]:
app.jinja_env.globals.update(get_icon=get_icon) app.jinja_env.globals.update(get_icon=get_icon)
def get_tz_info() -> Tuple[Optional[str], str, Set[str]]:
now = datetime.now().astimezone()
local_TZ = now.tzname()
local_UTC_offset = f'UTC{now.strftime("%z")}'
return local_TZ, local_UTC_offset, all_timezones_set
app.jinja_env.globals.update(tz_info=get_tz_info)
# ##### Generic/configuration methods ##### # ##### Generic/configuration methods #####
@app.after_request @app.after_request
@ -1067,6 +1084,23 @@ def capture_web():
if request.form.get('headers'): if request.form.get('headers'):
capture_query['headers'] = request.form['headers'] capture_query['headers'] = request.form['headers']
if request.form.get('timezone_id'):
capture_query['timezone_id'] = request.form['timezone_id']
if request.form.get('locale'):
capture_query['locale'] = request.form['locale']
if request.form.get('geo_longitude') and request.form.get('geo_latitude'):
capture_query['geolocation'] = {'longitude': float(request.form['geo_longitude']),
'latitude': float(request.form['geo_latitude'])}
if request.form.get('http_auth_username') and request.form.get('http_auth_password'):
capture_query['http_credentials'] = {'username': request.form['http_auth_username'],
'password': request.form['http_auth_password']}
if request.form.get('color_scheme'):
capture_query['color_scheme'] = request.form['color_scheme']
if request.form.get('proxy'): if request.form.get('proxy'):
parsed_proxy = urlparse(request.form['proxy']) parsed_proxy = urlparse(request.form['proxy'])
if parsed_proxy.scheme and parsed_proxy.hostname and parsed_proxy.port: if parsed_proxy.scheme and parsed_proxy.hostname and parsed_proxy.port:

View File

@ -251,6 +251,52 @@
</div> </div>
</div> </div>
<div class="row mb-3">
{% set local_TZ, local_UTC_offset, all_timezones = tz_info() %}
<label for="timezone_id" class="col-sm-2 col-form-label">Timezone (defaults to {{local_TZ}} - {{local_UTC_offset}}):</label>
<div class="col-sm-10">
<input class="form-control" list="tzOptions" name="timezone_id" id="timezone_id" aria-label="Pick the timezone for the capture">
<datalist id="tzOptions">
{% for tz in all_timezones %}
<option value="{{tz}}">{{tz}}</option>
{%endfor%}
</datalist>
</div>
</div>
<div class="row mb-3">
<label for="locale" class="col-sm-2 col-form-label">Locale:</label>
<div class="col-sm-10">
<input type="text" class="form-control" name="locale" id="locale" placeholder="Pass a language locale to the capture">
</div>
</div>
<div class="row mb-3">
<label for="color_scheme" class="col-sm-2 col-form-label">Color scheme:</label>
<div class="col-sm-10">
<select class="form-select" id="color_scheme" name="color_scheme" aria-label="Select a prefered color scheme">
<option selected>Select a color scheme</option>
<option value="light">Light</option>
<option value="dark">Dark</option>
<option value="no-preference">No preference</option>
</select>
</div>
</div>
<div class="row mb-3">
<label for="geoloc" class="col-sm-2 col-form-label">Geolocation:</label>
<div class="col-sm-10" id="geoloc">
<div class="row g-3">
<div class="col">
<input type="number" step="any" class="form-control" name="geo_longitude" id="geo_longitude" placeholder="Longitude">
</div>
<div class="col">
<input type="number" step="any" class="form-control" name="geo_latitude" id="geo_latitude" placeholder="Latitude">
</div>
</div>
</div>
</div>
<div class="row mb-3"> <div class="row mb-3">
<label for="proxy" class="col-sm-2 col-form-label">Proxy:</label> <label for="proxy" class="col-sm-2 col-form-label">Proxy:</label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -258,6 +304,27 @@
</div> </div>
</div> </div>
<div class="row mb-3">
<label for="httpauth" class="col-sm-2 col-form-label">HTTP Authentication</label>
<div class="col-sm-10" id="httpauth">
<div class="row g-3">
<div class="col">
<input type="text" class="form-control" name="http_auth_username" id="http_auth_username" placeholder="Username">
</div>
<div class="col">
<input type="text" class="form-control" name="http_auth_password" id="http_auth_password" placeholder="Password">
</div>
</div>
<div class="alert alert-danger" role="alert">
The authentication credentials will be stored on the lookyloo instance and potentially
accessed by third parties (either because the Lookyloo instance is public,
or people other than you have access to the instance).
If that's the case, please make sure none of them can be used to login as yourself
on websites.
</div>
</div>
</div>
<div class="row mb-3"> <div class="row mb-3">
<label for="cookies" class="col-sm-2 col-form-label">Cookies:</label> <label for="cookies" class="col-sm-2 col-form-label">Cookies:</label>
<div class="col-sm-10"> <div class="col-sm-10">