Coverage for webapp/handlers.py: 73%

45 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-17 22:07 +0000

1from flask import render_template 

2from webapp.config import SENTRY_DSN 

3 

4from canonicalwebteam.exceptions import ( 

5 StoreApiError, 

6 StoreApiResourceNotFound, 

7 StoreApiResponseDecodeError, 

8 StoreApiResponseError, 

9 StoreApiResponseErrorList, 

10 StoreApiTimeoutError, 

11 StoreApiConnectionError, 

12) 

13 

14from canonicalwebteam import image_template 

15 

16from webapp import helpers 

17 

18CSP = { 

19 "default-src": ["'self'"], 

20 "img-src": [ 

21 "'self'", 

22 "data: blob:", 

23 # This is needed to allow images from 

24 # https://www.google.*/ads/ga-audiences to load. 

25 "*", 

26 ], 

27 "script-src-elem": [ 

28 "'self'", 

29 "assets.ubuntu.com", 

30 "www.googletagmanager.com", 

31 "*.crazyegg.com", 

32 "w.usabilla.com", 

33 # This is necessary for Google Tag Manager to function properly. 

34 "'unsafe-inline'", 

35 ], 

36 "font-src": [ 

37 "'self'", 

38 "assets.ubuntu.com", 

39 ], 

40 "script-src": [ 

41 "'self'", 

42 "blob:", 

43 "'unsafe-eval'", 

44 "'unsafe-hashes'", 

45 ], 

46 "connect-src": [ 

47 "'self'", 

48 "sentry.is.canonical.com", 

49 "*.crazyegg.com", 

50 "analytics.google.com", 

51 "www.google-analytics.com", 

52 "stats.g.doubleclick.net", 

53 ], 

54 "frame-src": [ 

55 "'self'", 

56 "td.doubleclick.net", 

57 ], 

58 "style-src": [ 

59 "'self'", 

60 "'unsafe-inline'", 

61 ], 

62} 

63 

64 

65def rock_utility_processor(): 

66 """ 

67 This defines the set of properties and functions that will be added 

68 to the default context for processing templates. All these items 

69 can be used in all templates 

70 """ 

71 

72 account = None 

73 return { 

74 "schedule_banner": helpers.schedule_banner, 

75 "account": account, 

76 "image": image_template, 

77 "SENTRY_DSN": SENTRY_DSN, 

78 } 

79 

80 

81def set_handlers(app): 

82 @app.context_processor 

83 def utility_processor(): 

84 return rock_utility_processor() 

85 

86 # Error handlers 

87 # === 

88 @app.errorhandler(StoreApiTimeoutError) 

89 def handle_store_api_timeout(e): 

90 status_code = 504 

91 return ( 

92 render_template( 

93 "500.html", error_message=str(e), status_code=status_code 

94 ), 

95 status_code, 

96 ) 

97 

98 @app.errorhandler(StoreApiResourceNotFound) 

99 def handle_store_api_circuit_breaker_exception(e): 

100 return render_template("404.html", message=str(e)), 404 

101 

102 @app.errorhandler(StoreApiResponseErrorList) 

103 def handle_store_api_error_list(e): 

104 if e.status_code == 404: 

105 return render_template("404.html", message="Entity not found"), 404 

106 

107 status_code = 502 

108 if e.errors: 

109 errors = ", ".join([e.get("message") for e in e.errors]) 

110 return ( 

111 render_template( 

112 "500.html", error_message=errors, status_code=status_code 

113 ), 

114 status_code, 

115 ) 

116 

117 return ( 

118 render_template("500.html", status_code=status_code), 

119 status_code, 

120 ) 

121 

122 @app.errorhandler(StoreApiResponseDecodeError) 

123 @app.errorhandler(StoreApiResponseError) 

124 @app.errorhandler(StoreApiConnectionError) 

125 @app.errorhandler(StoreApiError) 

126 def handle_store_api_error(e): 

127 status_code = 502 

128 return ( 

129 render_template( 

130 "500.html", error_message=str(e), status_code=status_code 

131 ), 

132 status_code, 

133 ) 

134 

135 @app.after_request 

136 def add_headers(response): 

137 """ 

138 Security headers to add to all requests 

139 - Content-Security-Policy: Restrict resources (e.g., JavaScript, CSS, 

140 Images) and URLs 

141 - Referrer-Policy: Limit referrer data for security while preserving 

142 full referrer for same-origin requests 

143 - Cross-Origin-Embedder-Policy: allows embedding cross-origin 

144 resources without credentials 

145 - Cross-Origin-Opener-Policy: enable the page to open pop-ups while 

146 maintaining same-origin policy 

147 - Cross-Origin-Resource-Policy: allowing cross-origin requests to 

148 access the resource 

149 - X-Permitted-Cross-Domain-Policies: disallows cross-domain access to 

150 resources 

151 """ 

152 response.headers["Content-Security-Policy"] = helpers.get_csp_as_str( 

153 CSP 

154 ) 

155 response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin" 

156 response.headers["Cross-Origin-Embedder-Policy"] = "credentialless" 

157 response.headers["Cross-Origin-Opener-Policy"] = ( 

158 "same-origin-allow-popups" 

159 ) 

160 response.headers["Cross-Origin-Resource-Policy"] = "cross-origin" 

161 response.headers["X-Permitted-Cross-Domain-Policies"] = "none" 

162 return response