1515try : # Python 3
1616 from http .server import HTTPServer , BaseHTTPRequestHandler
1717 from urllib .parse import urlparse , parse_qs , urlencode
18+ from html import escape
1819except ImportError : # Fall back to Python 2
1920 from BaseHTTPServer import HTTPServer , BaseHTTPRequestHandler
2021 from urlparse import urlparse , parse_qs
2122 from urllib import urlencode
23+ from cgi import escape
2224
2325
2426logger = logging .getLogger (__name__ )
@@ -77,25 +79,37 @@ def _qs2kv(qs):
7779 for k , v in qs .items ()}
7880
7981
82+ def _is_html (text ):
83+ return text .startswith ("<" ) # Good enough for our purpose
84+
85+
86+ def _escape (key_value_pairs ):
87+ return {k : escape (v ) for k , v in key_value_pairs .items ()}
88+
89+
8090class _AuthCodeHandler (BaseHTTPRequestHandler ):
8191 def do_GET (self ):
8292 # For flexibility, we choose to not check self.path matching redirect_uri
8393 #assert self.path.startswith('/THE_PATH_REGISTERED_BY_THE_APP')
8494 qs = parse_qs (urlparse (self .path ).query )
8595 if qs .get ('code' ) or qs .get ("error" ): # So, it is an auth response
86- self . server . auth_response = _qs2kv (qs )
87- logger .debug ("Got auth response: %s" , self . server . auth_response )
96+ auth_response = _qs2kv (qs )
97+ logger .debug ("Got auth response: %s" , auth_response )
8898 template = (self .server .success_template
8999 if "code" in qs else self .server .error_template )
90- self ._send_full_response (
91- template .safe_substitute (** self .server .auth_response ))
100+ if _is_html (template .template ):
101+ safe_data = _escape (auth_response )
102+ else :
103+ safe_data = auth_response
104+ self ._send_full_response (template .safe_substitute (** safe_data ))
105+ self .server .auth_response = auth_response # Set it now, after the response is likely sent
92106 # NOTE: Don't do self.server.shutdown() here. It'll halt the server.
93107 else :
94108 self ._send_full_response (self .server .welcome_page )
95109
96110 def _send_full_response (self , body , is_ok = True ):
97111 self .send_response (200 if is_ok else 400 )
98- content_type = 'text/html' if body . startswith ( '<' ) else 'text/plain'
112+ content_type = 'text/html' if _is_html ( body ) else 'text/plain'
99113 self .send_header ('Content-type' , content_type )
100114 self .end_headers ()
101115 self .wfile .write (body .encode ("utf-8" ))
@@ -318,6 +332,7 @@ def __exit__(self, exc_type, exc_val, exc_tb):
318332 default = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize" )
319333 p .add_argument ('client_id' , help = "The client_id of your application" )
320334 p .add_argument ('--port' , type = int , default = 0 , help = "The port in redirect_uri" )
335+ p .add_argument ('--timeout' , type = int , default = 60 , help = "Timeout value, in second" )
321336 p .add_argument ('--host' , default = "127.0.0.1" , help = "The host of redirect_uri" )
322337 p .add_argument ('--scope' , default = None , help = "The scope list" )
323338 args = parser .parse_args ()
@@ -331,8 +346,8 @@ def __exit__(self, exc_type, exc_val, exc_tb):
331346 auth_uri = flow ["auth_uri" ],
332347 welcome_template =
333348 "<a href='$auth_uri'>Sign In</a>, or <a href='$abort_uri'>Abort</a" ,
334- error_template = "Oh no. $error" ,
349+ error_template = "<html> Oh no. $error</html> " ,
335350 success_template = "Oh yeah. Got $code" ,
336- timeout = 60 ,
351+ timeout = args . timeout ,
337352 state = flow ["state" ], # Optional
338353 ), indent = 4 ))
0 commit comments