I receive a list of links via http and I want to open them all in a new Firefox (or default browser) window as tabs; that is my script must not touch existing windows.
This is the best solution I could come up with:
from subprocess import call
links = GetListViaHTTP()
if len(links) == 1:
call(['firefox', '-new-window'] + links)
elif len(links) > 1:
call(['firefox'] + links)
else:
print('Nothing to do.')
This worked like a charm when I tested it, but putting something I received via http into subprocess.call
does not look save to me at all. So the question is:
How can I improve this code to avoid being vulnerable to MITM attacks?
If the list was not tampered with by an attacker, all links it contains will point to Stack Overflow answers, so maybe somehow validating the links with urllib.parse
is good enough?
-
\$\begingroup\$ Does the program have to open the link in firefox? Or can you open it in the default web-browser? \$\endgroup\$Peilonrayz– Peilonrayz ♦2017年03月24日 20:18:48 +00:00Commented Mar 24, 2017 at 20:18
-
\$\begingroup\$ @Peilonrayz Default webbrowser is fine too if it makes things easier. (It's technically even preferred, but I wanted to avoid my question being too broad. I'll gladly take either.) \$\endgroup\$Baum mit Augen– Baum mit Augen2017年03月24日 20:27:17 +00:00Commented Mar 24, 2017 at 20:27
2 Answers 2
Rather than implementing this yourself, Python by default has webbrowser
. This allows you to open one link in a; new tab, or new window. And so, to open all links in the list with the default web-browser you can:
import webbrowser
for link in links:
webbrowser.open_new_tab(link)
Since you say you don't trust the source of these urls, you can make sure they are actually URLs, and check there is no shell injection going on. This question says how you could do that.
And so I personally would use:
import webbrowser
import rfc3987
def open_links(links):
links = (
link
for link in links
if rfc3987.match(link, rule='IRI') is not None
)
# Get default webbrowser, change if you want to force a specific browser
browser = webbrowser.get()
try:
browser.open_new(next(links))
except StopIteration:
pass
else:
for link in links:
browser.open_new_tab(link)
-
\$\begingroup\$ Unfortunately,
open_new
opens new tabs in existing windows on my system (Arch Linux). I don't know why, maybe I botched my setup. But that rfc3987 package seems really useful to start with, thank you. (Btw,webbrowser.open_tab
does not seem to exist, I guess that's a typo?) \$\endgroup\$Baum mit Augen– Baum mit Augen2017年03月24日 20:43:39 +00:00Commented Mar 24, 2017 at 20:43 -
\$\begingroup\$ @BaummitAugen That's odd, it may just not be supported. But that was a typo, I'll fix it now, :) \$\endgroup\$2017年03月24日 20:46:53 +00:00Commented Mar 24, 2017 at 20:46
-
\$\begingroup\$ Addendum: When I turn off the "Open new windows in new tabs" option in firefox, I get a new tab for every link. Probably Firefox' fault? \$\endgroup\$Baum mit Augen– Baum mit Augen2017年03月24日 20:46:56 +00:00Commented Mar 24, 2017 at 20:46
-
\$\begingroup\$ Alright, after a little bit more research it appears to be the Browser's fault that
open_new
andopen_new_tab
are not handled properly. But validating the links with therfc3987
package sounds save enough (hope I am right :) ), so that counts as the answer. Thanks for your time! \$\endgroup\$Baum mit Augen– Baum mit Augen2017年03月24日 21:04:55 +00:00Commented Mar 24, 2017 at 21:04
You can also control a real browser with Python selenium bindings:
pip install selenium
Then, you can combine it with @Peilonrayz's link checking code:
from selenium import webdriver
# ...
driver = webdriver.Firefox()
try:
driver.get(next(links))
except StopIteration:
pass
else:
for link in links:
body = driver.find_element_by_tag_name("body")
body.send_keys(Keys.CONTROL + 't') # opens a new tab
driver.get(link)
(please replace CONTROL
with COMMAND
on Mac OS).
-
\$\begingroup\$ Thanks, I tried that too, but it is extremely slow and looks very clumsy compared to the CLI variant with
call
. \$\endgroup\$Baum mit Augen– Baum mit Augen2017年03月25日 10:59:00 +00:00Commented Mar 25, 2017 at 10:59 -
\$\begingroup\$ @BaummitAugen well, yes, there is a substantial overhead, but, if you would need to do something else except opening the links in the browser, webbrowser or a system call would not be an option anymore. For example, if you would need to click a specific element, or send a text to an input. But, if all you need is to open links in the browser, then yes, selenium is too much overhead. Thanks. \$\endgroup\$alecxe– alecxe2017年03月25日 14:07:57 +00:00Commented Mar 25, 2017 at 14:07
Explore related questions
See similar questions with these tags.