Compare commits

...

15 commits
1.1.1 ... main

3 changed files with 56 additions and 9 deletions

View file

@ -9,8 +9,11 @@ To make certain that you don't block just any instance in the Fediverse
because $somebody has it on their blocklist you assign _trust levels_ to because $somebody has it on their blocklist you assign _trust levels_ to
the correctness of the blocklists of the other servers. Only when a server the correctness of the blocklists of the other servers. Only when a server
is blocked with a total trust level that is above a confidence level, it is blocked with a total trust level that is above a confidence level, it
will be added to resulting blocklist automatically. Otherwise the user will be added to resulting blocklist automatically. Otherwise you will be
will be ask if they want to add a node to their blocklist or not. ask if you want to add a node to the blocklist or not.
And just to state the obvious: You should never blindly trust the blocklists
of your peers but do your own investications about in block when in doubt.
## Config file ## Config file
@ -94,7 +97,8 @@ domains = friendica.example.com
You can also add Mastodon instances you trust. In addition to the configuration You can also add Mastodon instances you trust. In addition to the configuration
needed for Friendica nodes you have to add the `type = mastodon` entry to the needed for Friendica nodes you have to add the `type = mastodon` entry to the
config section. config section. *Please note* that the used API endpoint is not available on
all Mastodon instances.
Please note only suspended entries from the Mastodon blocklist will be added to Please note only suspended entries from the Mastodon blocklist will be added to
the blocklist. Silenced entries will be ignored. the blocklist. Silenced entries will be ignored.

View file

@ -1,10 +1,16 @@
[tool.poetry] [tool.poetry]
name = "brewserverblocklist" name = "brewserverblocklist"
version = "1.1.1" version = "1.1.7"
description = "A python script to collect the server-wide blocklists from Friendica nodes to build a collection from trusted admin choice" description = "A python script to collect the server-wide blocklists from Friendica nodes to build a collection from trusted admin choice"
authors = ["Tobias Diekershoff"] authors = ["Tobias Diekershoff"]
license = "GNU General Public License v3.0" license = "GNU General Public License v3.0"
readme = "README.md" readme = "README.md"
include = [
{ path = "src/brewserverblocklist" },
]
packages = [
{ include = "brewserverblocklist", from = "src"},
]
[tool.poetry.scripts] [tool.poetry.scripts]
brewserverblocklist = "brewserverblocklist.brewserverblocklist:main" brewserverblocklist = "brewserverblocklist.brewserverblocklist:main"

View file

@ -14,6 +14,21 @@ import sys
from os.path import exists from os.path import exists
import requests import requests
class BParser(argparse.ArgumentParser):
"""
This expansion of the ArgParser class will display the --help results by
default if an error occurs (e.g. no arguments are passed to the script).
It is based on an StackOverflow answer from 2010 by unutbu who refered to
a reply from Steven Bethard as source of the code.
https://stackoverflow.com/questions/4042452/display-help-message-with-python-argparse-when-script-is-called-without-any-argu/4042861#4042861
"""
def error(self, message):
sys.stderr.write('error: %s\n' % message)
self.print_help()
sys.exit(2)
class BrewBlocklist(): class BrewBlocklist():
""" """
This is the cauldron that is used to This is the cauldron that is used to
@ -32,10 +47,14 @@ class BrewBlocklist():
self.auto_accept = auto_accept self.auto_accept = auto_accept
self.auto_accept_direction = auto_accept_direction self.auto_accept_direction = auto_accept_direction
self.safe_harbor = [] self.safe_harbor = []
self.error = []
config = configparser.RawConfigParser() config = configparser.RawConfigParser()
config.read(configfile) config.read(configfile)
for section in config.sections(): for section in config.sections():
section_values = dict(config.items(section)) section_values = dict(config.items(section))
if (section.find('http://') > -1) or (section.find('https://') > -1):
print('The section name in the config file must not contain the protocol ({})'.format(section))
sys.exit(1)
if not section == 'safe harbor': if not section == 'safe harbor':
if not 'type' in section_values.keys(): if not 'type' in section_values.keys():
section_values['type'] = 'friendica' section_values['type'] = 'friendica'
@ -62,6 +81,9 @@ class BrewBlocklist():
if source['type'] == 'friendica': if source['type'] == 'friendica':
# Friendica publishes the blocklist as CSV file # Friendica publishes the blocklist as CSV file
requ = requests.get('https://{}/blocklist/domain/download'.format(source['url'])) requ = requests.get('https://{}/blocklist/domain/download'.format(source['url']))
if not requ.status_code == requests.codes.ok:
self.error.append('The request to {} failed'.format(sources['url']))
break
for line in requ.text.split('\n'): for line in requ.text.split('\n'):
try: try:
pattern, reason = line.split(',') pattern, reason = line.split(',')
@ -75,9 +97,15 @@ class BrewBlocklist():
elif source['type'] == 'mastodon': elif source['type'] == 'mastodon':
# Mastodon has an API endpoint that contains the information # Mastodon has an API endpoint that contains the information
requ = requests.get('https://{}//api/v1/instance/domain_blocks'.format(source['url'])) requ = requests.get('https://{}//api/v1/instance/domain_blocks'.format(source['url']))
for item in requ.json(): if not requ.status_code == requests.codes.ok:
self.blocklist[item['domain']] = self.blocklist.get(item['domain'], 0) + source['trust'] self.error.append('The request to {} failed'.format(sources['url']))
self.reasons[item['domain']] = self.reasons.get(item['domain'], item['comment']) break
try:
for item in requ.json():
self.blocklist[item['domain']] = self.blocklist.get(item['domain'], 0) + source['trust']
self.reasons[item['domain']] = self.reasons.get(item['domain'], item['comment'])
except:
self.error.append('{} returned no valid json to the API call'.format(source['url']))
else: else:
raise ValueError('{} is not a supported node type, check your config file'.format(source['type'])) raise ValueError('{} is not a supported node type, check your config file'.format(source['type']))
@ -125,10 +153,19 @@ class BrewBlocklist():
orig_stdout = sys.stdout orig_stdout = sys.stdout
sys.stdout = out_file sys.stdout = out_file
for key, value in self.blocklist.items(): for key, value in self.blocklist.items():
try:
if ("," in self.reasons[key] or " " in self.reasons[key]) and not self.reasons[key].startswith('"'):
self.reasons[key] = '"{}"'.format(self.reasons[key])
except TypeError:
self.reasons[key] = '"no reason given"'
self.error.append("for {} no blocking reason was provided".format(key))
print('{}, {}, {}'.format(key, self.reasons[key], value)) print('{}, {}, {}'.format(key, self.reasons[key], value))
if self.outputfile: if self.outputfile:
sys.stdout = orig_stdout sys.stdout = orig_stdout
out_file.close() out_file.close()
if len(self.error):
print("\n\nWhile creating the blocklist the following problems occured:")
print("\n".join(self.error))
def main(): def main():
""" """
@ -140,7 +177,7 @@ def main():
* collect the ingredient * collect the ingredient
* serve the result * serve the result
""" """
parser = argparse.ArgumentParser() parser = BParser()
parser.add_argument('-c', '--config', parser.add_argument('-c', '--config',
dest='configfile', dest='configfile',
required=True, required=True,
@ -168,7 +205,7 @@ def main():
arg_auto_accept = not args.auto_accept_direction is None arg_auto_accept = not args.auto_accept_direction is None
if not exists(args.configfile): if not exists(args.configfile):
print('The config file {} was not found.'.format(args.configfile)) print('The config file {} was not found.'.format(args.configfile))
sys.exit() sys.exit(1)
brew = BrewBlocklist(args.configfile, args.outputfile, arg_auto_accept, brew = BrewBlocklist(args.configfile, args.outputfile, arg_auto_accept,
args.auto_accept_direction, args.confidence) args.auto_accept_direction, args.confidence)
brew.collect_ingrediens() brew.collect_ingrediens()