*new_url = *old_url;
return;
}
+ // Check Access control
+ if (!res_manager->AllowedResource(*old_url)) {
+ // deined resource
+ *new_url = "about:blank";
+ return;
+ }
*new_url = res_manager->GetLocalizedPath(*old_url);
}
return content_info_;
}
+std::shared_ptr<const wgt::parse::WarpInfo>
+ ApplicationData::warp_info() const {
+ return warp_info_;
+}
+
+std::shared_ptr<const wgt::parse::CSPInfo>
+ ApplicationData::csp_info() const {
+ return csp_info_;
+}
+
+std::shared_ptr<const wgt::parse::CSPInfo>
+ ApplicationData::csp_report_info() const {
+ return csp_report_info_;
+}
+
bool ApplicationData::LoadManifestData() {
std::string config_xml_path(application_path_ + kConfigXml);
SPLASH_SCREEN_HANDLER,
TIZEN_APPLICATION_HANDLER,
WIDGET_HANDLER,
- CONTENT_HANDLER
+ CONTENT_HANDLER,
+ WARP_HANDLER,
+ CSP_HANDLER,
+ CSP_REPORT_HANDLER
};
std::vector<parser::ManifestHandler*> handlers = {
new wgt::parse::SplashScreenHandler, // SPLASH_SCREEN_HANDLER
new wgt::parse::TizenApplicationHandler, // TIZEN_APPLICATION_HANDLER
new wgt::parse::WidgetHandler, // WIDGET_HANDLER
- new wgt::parse::ContentHandler // CONTENT_HANDLER
+ new wgt::parse::ContentHandler, // CONTENT_HANDLER
+ new wgt::parse::WarpHandler, // WARP_HANDLER
+ // CSP_HANDLER
+ new wgt::parse::CSPHandler(wgt::parse::CSPHandler::SecurityType::CSP),
+ // CSP_REPORT_HANDLER
+ new wgt::parse::CSPHandler(
+ wgt::parse::CSPHandler::SecurityType::CSP_REPORT_ONLY)
};
std::unique_ptr<parser::ManifestHandlerRegistry> registry;
manifest_parser.GetManifestData(
handlers[ManifestHandlerType::CONTENT_HANDLER]->Key()));
+ warp_info_ =
+ std::static_pointer_cast<const wgt::parse::WarpInfo>(
+ manifest_parser.GetManifestData(
+ handlers[ManifestHandlerType::WARP_HANDLER]->Key()));
+
+ csp_info_ =
+ std::static_pointer_cast<const wgt::parse::CSPInfo>(
+ manifest_parser.GetManifestData(
+ handlers[ManifestHandlerType::CSP_HANDLER]->Key()));
+
+ csp_report_info_ =
+ std::static_pointer_cast<const wgt::parse::CSPInfo>(
+ manifest_parser.GetManifestData(
+ handlers[ManifestHandlerType::CSP_REPORT_HANDLER]->Key()));
+
for (auto iter = handlers.begin(); iter != handlers.end(); ++iter) {
delete *iter;
}
#include <manifest_handlers/tizen_application_handler.h>
#include <manifest_handlers/widget_handler.h>
#include <manifest_handlers/content_handler.h>
+#include <manifest_handlers/warp_handler.h>
+#include <manifest_handlers/csp_handler.h>
#include <memory>
widget_info() const;
std::shared_ptr<const wgt::parse::ContentInfo>
content_info() const;
+ std::shared_ptr<const wgt::parse::WarpInfo>
+ warp_info() const;
+ std::shared_ptr<const wgt::parse::CSPInfo>
+ csp_info() const;
+ std::shared_ptr<const wgt::parse::CSPInfo>
+ csp_report_info() const;
const std::string application_path() const { return application_path_; }
const std::string pkg_id() const { return pkg_id_; }
widget_info_;
std::shared_ptr<const wgt::parse::ContentInfo>
content_info_;
+ std::shared_ptr<const wgt::parse::WarpInfo>
+ warp_info_;
+ std::shared_ptr<const wgt::parse::CSPInfo>
+ csp_info_;
+ std::shared_ptr<const wgt::parse::CSPInfo>
+ csp_report_info_;
std::string application_path_;
std::string pkg_id_;
// Scheme type
const char* kSchemeTypeApp = "app://";
const char* kSchemeTypeFile = "file://";
+const char* kSchemeTypeHttp = "http://";
+const char* kSchemeTypeHttps = "https://";
// TODO(wy80.choi): comment out below unused const variables if needed.
-// const char* kSchemeTypeHttp = "http://";
-// const char* kSchemeTypeHttps = "https://";
// const char* kSchemeTypeWidget = "widget://";
// Default Start Files
}
}
+static void GetURLInfo(const std::string& url,
+ std::string* scheme,
+ std::string* domain,
+ std::string* port) {
+ size_t end_of_scheme = url.find_first_of(':');
+ if (end_of_scheme == std::string::npos) {
+ end_of_scheme = -1;
+ } else {
+ *scheme = url.substr(0, end_of_scheme);
+ }
+
+ size_t start_of_domain = url.find_first_not_of('/', end_of_scheme+1);
+ size_t end_of_domain = url.find_first_of('/', start_of_domain);
+ *domain = url.substr(start_of_domain,
+ end_of_domain == std::string::npos ?
+ std::string::npos : end_of_domain - start_of_domain);
+ size_t port_separator = (*domain).find_first_of(':');
+ if (port_separator != std::string::npos) {
+ *port = (*domain).substr(port_separator+1);
+ *domain = (*domain).substr(0, port_separator);
+ } else {
+ *port = "80";
+ }
+}
+
} // namespace
ResourceManager::Resource::Resource(const std::string& uri)
: application_data_(application_data), locale_manager_(locale_manager) {
if (application_data != NULL) {
appid_ = application_data->tizen_application_info()->id();
+ if (application_data->csp_info() != NULL ||
+ application_data->csp_report_info() != NULL ||
+ application_data->allowed_navigation_info() != NULL) {
+ security_model_version_ = 2;
+ } else {
+ security_model_version_ = 1;
+ }
}
}
return ret;
}
+bool ResourceManager::AllowNavigation(const std::string& url) {
+ if (security_model_version_ == 2)
+ return CheckAllowNavigation(url);
+ return CheckWARP(url);
+}
+
+bool ResourceManager::AllowedResource(const std::string& url) {
+ if (security_model_version_ == 2)
+ return true;
+ return CheckWARP(url);
+}
+
+bool ResourceManager::CheckWARP(const std::string& url) {
+ // allow non-external resource
+ if (!utils::StartsWith(url, kSchemeTypeHttp) &&
+ !utils::StartsWith(url, kSchemeTypeHttps)) {
+ return true;
+ }
+
+ auto warp = application_data_->warp_info();
+ if (warp.get() == NULL)
+ return false;
+
+ auto find = warp_cache_.find(url);
+ if (find != warp_cache_.end()) {
+ return find->second;
+ }
+
+ bool& result = warp_cache_[url];
+ result = true;
+
+ // if didn't have a scheme, it means local resource
+ size_t end_of_scheme = url.find_first_of(':');
+ if (end_of_scheme == std::string::npos) {
+ return true;
+ }
+
+ std::string scheme;
+ std::string domain;
+ std::string port;
+ GetURLInfo(url, &scheme, &domain, &port);
+
+ for (auto& allow : warp->access_map()) {
+ if (allow.first == "*") {
+ return true;
+ }
+ std::string a_scheme, a_domain, a_port;
+ GetURLInfo(allow.first, &a_scheme, &a_domain, &a_port);
+
+ // should be match the scheme and port
+ if (a_scheme != scheme || a_port != port) {
+ continue;
+ }
+
+ // if domain alos was matched, allow resource
+ if (a_domain == domain) {
+ return true;
+ } else if (allow.second) {
+ // if does not match domain, should be check sub domain
+
+ // filter : test.com , subdomain=true
+ // url : aaa.test.com
+ // check url was ends with ".test.com"
+ a_domain = "." + a_domain;
+ if (utils::EndsWith(domain, a_domain)) {
+ return true;
+ }
+ }
+ }
+
+ return result = false;
+}
+
+bool ResourceManager::CheckAllowNavigation(const std::string& url) {
+ // allow non-external resource
+ if (!utils::StartsWith(url, kSchemeTypeHttp) &&
+ !utils::StartsWith(url, kSchemeTypeHttps)) {
+ return true;
+ }
+
+ auto allow = application_data_->allowed_navigation_info();
+ if (allow.get() == NULL)
+ return false;
+
+ auto find = warp_cache_.find(url);
+ if (find != warp_cache_.end()) {
+ return find->second;
+ }
+
+ bool& result = warp_cache_[url];
+ result = true;
+
+ // if didn't have a scheme, it means local resource
+ size_t end_of_scheme = url.find_first_of(':');
+ if (end_of_scheme == std::string::npos) {
+ return true;
+ }
+
+ std::string scheme;
+ std::string domain;
+ std::string port;
+ GetURLInfo(url, &scheme, &domain, &port);
+
+ for (auto& allow_domain : allow->GetAllowedDomains()) {
+ std::string a_scheme;
+ std::string a_domain;
+ std::string a_port;
+ GetURLInfo(allow_domain, &a_scheme, &a_domain, &a_port);
+
+ // check wildcard *
+ if (a_domain == "*") {
+ return true;
+ }
+
+ bool prefix_wild = false;
+ bool suffix_wild = false;
+ if (utils::StartsWith(a_domain, "*.")) {
+ prefix_wild = true;
+ // *.domain.com -> .domain.com
+ a_domain = a_domain.substr(1);
+ }
+ if (utils::EndsWith(a_domain, ".*")) {
+ suffix_wild = true;
+ // domain.* -> domain.
+ a_domain = a_domain.substr(0, a_domain.length() - 1);
+ }
+
+ if (!prefix_wild && !suffix_wild) {
+ // if no wildcard, should be exactly matched
+ if (domain == a_domain) {
+ return true;
+ }
+ } else if (prefix_wild && !suffix_wild) {
+ // *.domain.com : it shoud be "domain.com" or end with ".domain.com"
+ if (domain == a_domain.substr(1) ||
+ utils::EndsWith(domain, a_domain)) {
+ return true;
+ }
+ } else if (!prefix_wild && suffix_wild) {
+ // www.sample.* : it should be starts with "www.sample."
+ if (utils::StartsWith(domain, a_domain)) {
+ return true;
+ }
+ } else if (prefix_wild && suffix_wild) {
+ // *.sample.* : it should be starts with sample. or can find ".sample."
+ // in url
+ if (utils::StartsWith(domain, a_domain.substr(1)) ||
+ std::string::npos != domain.find(a_domain)) {
+ return true;
+ }
+ }
+ }
+
+ return result = false;
+}
+
} // namespace wrt
// output : /[system path]/.../locales/.../
std::string GetLocalizedPath(const std::string& origin);
std::unique_ptr<Resource> GetStartResource(const AppControl* app_control);
+ bool AllowNavigation(const std::string& url);
+ bool AllowedResource(const std::string& url);
void set_base_resource_path(const std::string& base_path);
std::string GetDefaultOrEmpty();
// for localization
bool Exists(const std::string& path);
+ bool CheckWARP(const std::string& url);
+ bool CheckAllowNavigation(const std::string& url);
std::string resource_base_path_;
std::string appid_;
std::map<const std::string, bool> file_existed_cache_;
std::map<const std::string, std::string> locale_cache_;
+ std::map<const std::string, bool> warp_cache_;
+
ApplicationData* application_data_;
LocaleManager* locale_manager_;
+ int security_model_version_;
};
} // namespace wrt
const char* kBackgroundMusicFeature = "background,music";
const char* kSoundModeFeature = "sound,mode";
const char* kBackgroundVibrationFeature = "background,vibration";
+const char* kCSPFeature = "csp";
+
const char* kGeolocationPermissionPrefix = "__WRT_GEOPERM_";
const char* kNotificationPermissionPrefix = "__WRT_NOTIPERM_";
const char* kQuotaPermissionPrefix = "__WRT_QUOTAPERM_";
const char* kCertificateAllowPrefix = "__WRT_CERTIPERM_";
const char* kDBPrivateSection = "private";
+const char* kDefaultCSPRule =
+ "default-src *; script-src 'self'; style-src 'self'; object-src 'none';";
bool FindPrivilege(wrt::ApplicationData* app_data,
const std::string& privilege) {
// TODO(sngn.lee): find the proxy url
// ewk_context_proxy_uri_set(ewk_context_, ... );
-
- // TODO(sngn.lee): check csp element in config.xml and enable - "csp"
+ if (app_data_->csp_info() != NULL ||
+ app_data_->csp_report_info() != NULL ||
+ app_data_->allowed_navigation_info() != NULL) {
+ security_model_version_ = 2;
+ if (app_data_->csp_info() == NULL ||
+ app_data_->csp_info()->security_rules().empty()) {
+ csp_rule_ = kDefaultCSPRule;
+ } else {
+ csp_rule_ = app_data_->csp_info()->security_rules();
+ }
+ if (app_data_->csp_report_info() != NULL &&
+ !app_data_->csp_report_info()->security_rules().empty()) {
+ csp_report_rule_ = app_data_->csp_report_info()->security_rules();
+ }
+ ewk_context_tizen_extensible_api_string_set(ewk_context_,
+ kCSPFeature,
+ EINA_TRUE);
+ } else {
+ security_model_version_ = 1;
+ }
return true;
}
void WebApplication::SetupWebView(WebView* view) {
view->SetEventListener(this);
+
+ // Setup CSP Rule
+ if (security_model_version_ == 2) {
+ view->SetCSPRule(csp_rule_, false);
+ if (!csp_report_rule_.empty()) {
+ view->SetCSPRule(csp_report_rule_, true);
+ }
+ }
+
// TODO(sngn.lee): set UserAgent to WebView
- // TODO(sngn.lee): set CSP
}
bool WebApplication::OnDidNavigation(WebView* /*view*/,
- const std::string& /*url*/) {
+ const std::string& url) {
// TODO(sngn.lee): scheme handling
// except(file , http, https, app) pass to appcontrol and return false
- return true;
+
+ return resource_manager_->AllowNavigation(url);
}
void WebApplication::OnNotificationPermissionRequest(
std::unique_ptr<ResourceManager> resource_manager_;
std::unique_ptr<wrt::AppControl> received_appcontrol_;
std::function<void(void)> terminator_;
+ int security_model_version_;
+ std::string csp_rule_;
+ std::string csp_report_rule_;
};
} // namespace wrt
bool WebView::SetUserAgent(const std::string& user_agent) {
return impl_->SetUserAgent(user_agent.c_str());
}
+
+void WebView::SetCSPRule(const std::string& rule, bool report_only) {
+ impl_->SetCSPRule(rule, report_only);
+}
+
} // namespace wrt
bool EvalJavascript(const std::string& script);
void SetAppInfo(const std::string& app_name, const std::string& version);
bool SetUserAgent(const std::string& user_agent);
+ void SetCSPRule(const std::string& rule, bool report_only);
void SetEventListener(EventListener* listener);
Evas_Object* evas_object() const;
return ewk_view_user_agent_set(ewk_view_, user_agent.c_str());
}
+void WebViewImpl::SetCSPRule(const std::string& rule, bool report_only) {
+ ewk_view_content_security_policy_set(
+ ewk_view_,
+ rule.c_str(),
+ report_only ? EWK_REPORT_ONLY : EWK_ENFORCE_POLICY);
+}
+
+
} // namespace wrt
bool EvalJavascript(const std::string& script);
void SetAppInfo(const std::string& app_name, const std::string& version);
bool SetUserAgent(const std::string& user_agent);
+ void SetCSPRule(const std::string& rule, bool report_only);
void SetEventListener(WebView::EventListener* listener);
Evas_Object* evas_object() const;