diff --git a/data-sources/instagram.py b/data-sources/instagram.py index 5826c25..7e4bd7a 100644 --- a/data-sources/instagram.py +++ b/data-sources/instagram.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 import json import sys -import urllib.parse import requests +import re from typing import Optional import cryptography.hazmat.primitives.asymmetric.utils as crypto from cryptography.hazmat.primitives.asymmetric import ec @@ -34,15 +34,26 @@ def __init__(self, address: str, pub_key: str, value: str, signature: str): self.value = value -def get_user_data(data: CallData) -> Optional[VerificationData]: +def get_urls_from_biography(user: str) -> [str]: """ - Tries getting the verification data for the user having the given Instagram username. - :param data: Data used to get the VerificationData - :return: An OptionalData object if the call was successful, or None if it errored somehow. + Returns all the URLs that are found inside the biography of the user having the given user username. + :param user: Username of the Instagram user. + :return: List of URLs that are found inside the biography + """ + url = f"{ENDPOINT}/users/{user}" + result = requests.request("GET", url, headers=HEADERS).json() + return re.findall(r'(https?://[^\s]+)', result['biography']) + + +def get_signature_from_url(url: str) -> Optional[VerificationData]: + """ + Tries getting the signature object linked to the given URL. + :param url: URL that should contain the signature object. + :return: A dictionary containing 'valid' to tell whether the search was valid, and an optional 'data' containing + the signature object. """ try: - url_encoded_username = urllib.parse.quote(data.username) - result = requests.request("GET", f"{ENDPOINT}/{url_encoded_username}", headers=HEADERS).json() + result = requests.request("GET", url, headers=HEADERS).json() if validate_json(result): return VerificationData( result['address'], @@ -142,12 +153,24 @@ def main(args: str): json_obj = json.loads(decoded) call_data = check_values(json_obj) - result = get_user_data(call_data) - if result is None: - raise Exception(f"No valid signature data found for user with username {call_data.username}") - + # Get the URLs to check from the user biography + urls = get_urls_from_biography(call_data.username) + if len(urls) == 0: + raise Exception(f"No URL found inside {call_data.username} biography") + + # Find the signature following the URLs + data = None + for url in urls: + result = get_signature_from_url(url) + if result is not None: + data = result + break + + if data is None: + raise Exception(f"No valid signature data found inside {call_data.username} biography") + # Verify the signature - signature_valid = verify_signature(result) + signature_valid = verify_signature(data) if not signature_valid: raise Exception("Invalid signature") diff --git a/data-sources/instagram_test.py b/data-sources/instagram_test.py index 6f96745..8ea6960 100644 --- a/data-sources/instagram_test.py +++ b/data-sources/instagram_test.py @@ -1,31 +1,49 @@ import unittest -import instagram + import httpretty +import instagram -class instagramTest(unittest.TestCase): + +class TestInstagram(unittest.TestCase): @httpretty.activate(verbose=True, allow_net_connect=False) - def test_get_user_data(self): + def test_get_urls_from_biography(self): # Register fake HTTP call httpretty.register_uri( httpretty.GET, - "https://themis.mainnet.desmos.network/instagram/test_user_desmos", + "https://themis.mainnet.desmos.network/instagram/users/riccardomontagnin", status=200, - body='{"address":"71b0310267b49279116835ed35791c24c110012f","pub_key":"0203233fabd69a1b7a90bb968a0ab66e3af61989f65cf0bc1f8e9518740a302f1f","value":"746573745f757365725f6465736d6f73","signature":"c12605456b8652df655bb43d0166586dfc0c5d758b03f127ca6b027d0ec140ca29b9569a20c9b78b72e13d15c1a7fa0b142dc0e624f3f51ef76bd94e55345d2a"}', + body='{"biography":"https://pastebin.com/raw/D95NyR91"}', + ) + + url = instagram.get_urls_from_biography('riccardomontagnin') + self.assertEqual(['https://pastebin.com/raw/TgSpUCz6'], url) + + @httpretty.activate(verbose=True, allow_net_connect=False) + def test_get_signature_from_url(self): + # Register fake HTTP call + httpretty.register_uri( + httpretty.GET, + "https://pastebin.com/raw/xz4S8WrW", + status=200, + body='{"address":"desmos13yp2fq3tslq6mmtq4628q38xzj75ethzela9uu","pub_key":"033024e9e0ad4f93045ef5a60bb92171e6418cd13b082e7a7bc3ed05312a0b417d","signature":"a00a7d5bd45e42615645fcaeb4d800af22704e54937ab235e5e50bebd38e88b765fdb696c22712c0cab1176756b6346cbc11481c544d1f7828cb233620c06173","value":"ricmontagnin"}', ) # Valid signature - data = instagram.get_user_data(instagram.CallData('test_user_desmos')) + data = instagram.get_signature_from_url('https://pastebin.com/raw/xz4S8WrW') self.assertIsNotNone(data) - # Invalid signature + # Register fake HTTP call httpretty.register_uri( httpretty.GET, - "https://themis.mainnet.desmos.network/instagram/test_user_desmos", - status=404, + "https://bitcoin.org", + status=200, + body='Bitcoin website', ) - data = instagram.get_user_data(instagram.CallData('test_user_desmos')) + + # Invalid signature + data = instagram.get_signature_from_url('https://bitcoin.org') self.assertIsNone(data) def test_validate_json(self):