auto-response-header supersede previous content-security-policy setting.
This setting is a list of "pattern name=value" entries. When a pattern
match a requested uri, the HTTP header "name: value" is added to
response.
.TP
.B RequestQueued
Fired before each request (and so, multiple times in one page: one time for
-each image, css, scripts, frames...). This is the right event to set
-`content-security-policy' setting.
+each image, css, scripts, frames...).
.TP
.B DownloadStart
Fired right before a download is started. This is fired for vimb downloads as
.PD
.RE
.TP
-.B content-security-policy (string)
-Prepend a `Content-Security-Policy' HTTP-Header to responses received from
-server. This setting has to be setted early if managed with `autocmd' (at
-RequestQueued), in order to be managed by webkit.
+.B auto-response-header (list)
+Prepend HTTP-Header to responses received from server, based on pattern
+matching. The purpose of this setting is to enforce some security setting in
+the client. For example, you could set Content-Security-Policy (see
+`http://www.w3.org/TR/CSP/') for implement a whitelist policy, or set
+Strict-Transport-Security for server that don't provide this header whereas
+they propose https website.
-It could be used to implement a whitelist policy for visited uri.
+Note that this setting will not remplace existing headers, but add a new one.
+If multiple patterns match a request uri, the last matched rule will be
+applied. You could also specified differents headers for same pattern.
-Note that this setting will not remplace existing headers, but add one.
-
-Please refer to `http://www.w3.org/TR/CSP/' for syntax.
+The format is: `pattern name=value`. For each request matching `pattern`, an
+HTTP header "name: value" will be added to the response.
.RS
.PP
Example:
.PD 0
-.IP ":set content-security-policy=default-src 'self' 'unsafe-inline' 'unsafe-eval'; script-src 'none'"
-Webkit will see the `Content-Security-Policy' header defined with each response.
+.IP ":set auto-response-header=* Content-security-policy=default-src 'self' 'unsafe-inline' 'unsafe-eval'; script-src 'none'
+.IP ":set auto-response-header+=https://example.com/* Content-security-policy=default-src 'self' https://*.example.com/
+.IP ":set auto-response-header+=https://example.com/* Strict-Transport-Security=max-age=31536000
.PD
.RE
.TP
--- /dev/null
+/**
+ * vimb - a webkit based vim like browser.
+ *
+ * Copyright (C) 2012-2014 Daniel Carl
+ * Copyright (C) 2014 Sébastien Marie
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ */
+#include "config.h"
+#include "ascii.h"
+#include "arh.h"
+#include "util.h"
+
+typedef struct {
+ char *pattern; /* pattern the uri is matched against */
+ char *name; /* header name */
+ char *value; /* header value */
+} MatchARH;
+
+static void marh_free(MatchARH *);
+static char *read_pattern(char **parse);
+static char *read_name(char **parse);
+static char *read_value(char **parse);
+
+
+/**
+ * parse the data string to a list of MatchARH
+ *
+ * pattern name=value[,...]
+ */
+GSList *arh_parse(const char *data, const char **error)
+{
+ GSList *parsed = NULL;
+ char *data_dup = g_strdup(data);
+ char *parse = data_dup;
+
+ while (*parse) {
+ char *pattern = read_pattern(&parse);
+ char *name = read_name(&parse);
+ char *value = read_value(&parse);
+
+ if (pattern && name && value) {
+ MatchARH *marh = g_slice_new0(MatchARH);
+
+ marh->pattern = g_strdup(pattern);
+ marh->name = g_strdup(name);
+ marh->value = g_strdup(value);
+
+ parsed = g_slist_append(parsed, marh);
+
+ PRINT_DEBUG("append pattern='%s' name='%s' value='%s'",
+ marh->pattern, marh->name, marh->value);
+
+ } else {
+ /* one error occurs: free already parsed list */
+ arh_free(parsed);
+
+ /* set error if asked */
+ if ( error != NULL ) {
+ *error = "syntax error";
+ }
+
+ g_free(data_dup);
+ return NULL;
+ }
+ }
+
+ g_free(data_dup);
+ return parsed;
+}
+
+
+/**
+ * free the list of MatchARH
+ */
+void arh_free(GSList *list)
+{
+ g_slist_free_full(list, (GDestroyNotify)marh_free);
+}
+
+
+/**
+ * append to reponse-header of SoupMessage,
+ * the header that match the specified uri
+ */
+void arh_run(GSList *list, const char *uri, SoupMessage *msg)
+{
+ PRINT_DEBUG("uri: %s", uri);
+
+ /* walk thought the list */
+ for (GSList *l=list; l; l = g_slist_next(l)) {
+ MatchARH *marh = (MatchARH *)l->data;
+
+ if (util_wildmatch(marh->pattern, uri)) {
+ /* the pattern match, so replace headers
+ * as side-effect, for one header the last matched will be keeped */
+ soup_message_headers_replace(msg->response_headers,
+ marh->name, marh->value);
+
+ PRINT_DEBUG(" header added: %s: %s", marh->name, marh->value);
+ }
+ }
+}
+
+
+/**
+ * free a MatchARH
+ */
+static void marh_free(MatchARH *marh)
+{
+ if (marh) {
+ g_free(marh->pattern);
+ g_free(marh->name);
+ g_free(marh->value);
+
+ g_slice_free(MatchARH, marh);
+ }
+}
+
+/**
+ * read until ' ' (or any other SPACE)
+ */
+static char *read_pattern(char **line)
+{
+ char *pattern;
+
+ if (!*line || !**line) {
+ return NULL;
+ }
+
+ /* remember where the pattern starts */
+ pattern = *line;
+
+ /* move pointer to the end of the pattern (or end-of-line) */
+ while (**line && !VB_IS_SPACE(**line)) {
+ (*line)++;
+ }
+
+ if (**line) {
+ /* end the pattern */
+ *(*line)++ = '\0';
+
+ /* skip trailing whitespace */
+ while (VB_IS_SPACE(**line)) {
+ (*line)++;
+ }
+
+ return pattern;
+
+ } else {
+ /* end-of-line was encounter */
+ return NULL;
+ }
+}
+
+/**
+ * read until '='
+ */
+static char *read_name(char **line)
+{
+ char *name;
+
+ if (!*line || !**line) {
+ return NULL;
+ }
+
+ /* remember where the name starts */
+ name = *line;
+
+ /* move pointer to the end of the name (or end-of-line) */
+ while (**line && **line != '=') {
+ (*line)++;
+ }
+
+ if (**line) {
+ /* end the name */
+ *(*line)++ = '\0';
+ return name;
+
+ } else {
+ /* end-of-line was encounter */
+ return NULL;
+ }
+}
+
+/**
+ * read until ',' or end-of-line
+ */
+static char *read_value(char **line)
+{
+ char *value;
+
+ if (!*line || !**line) {
+ return NULL;
+ }
+
+ /* remember where the value starts */
+ value = *line;
+
+ /* move pointer to the end of the value or of the line */
+ while (**line && **line != ',') {
+ (*line)++;
+ }
+
+ /* end the value */
+ if (**line) {
+ *(*line)++ = '\0';
+ }
+
+ return value;
+}
--- /dev/null
+/**
+ * vimb - a webkit based vim like browser.
+ *
+ * Copyright (C) 2012-2014 Daniel Carl
+ * Copyright (C) 2014 Sébastien Marie
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/.
+ */
+#include "config.h"
+
+#ifndef _ARH_H
+#define _ARH_H
+
+#include "main.h"
+
+GSList *arh_parse(const char *, const char **);
+void arh_free(GSList *);
+void arh_run(GSList *, const char *, SoupMessage *);
+
+#endif /* end of include guard: _ARH_H */
#include "bookmark.h"
#include "js.h"
#include "autocmd.h"
+#include "arh.h"
/* variables */
static char **args;
static void session_request_queued_cb(SoupSession *session, SoupMessage *msg, gpointer data)
{
-#ifdef FEATURE_AUTOCMD
SoupURI *suri = soup_message_get_uri(msg);
char *uri = soup_uri_to_string(suri, false);
+#ifdef FEATURE_AUTOCMD
autocmd_run(AU_REQUEST_QUEUED, uri, NULL);
-
- g_free(uri);
#endif
+
#ifdef DEBUG
SoupMessageHeadersIter iter;
const char *name, *value;
}
#endif
- /* add a fake Content-Security-Policy header in server-response header */
- if (vb.config.contentsecuritypolicy && *vb.config.contentsecuritypolicy != '\0') {
- soup_message_headers_append(msg->response_headers, "Content-Security-Policy",
- vb.config.contentsecuritypolicy);
- }
+ arh_run(vb.config.autoresponseheader, uri, msg);
+ g_free(uri);
}
static gboolean autocmdOptionArgFunc(const gchar *option_name, const gchar *value, gpointer data, GError **error)
guint timeoutlen; /* timeout for ambiguous mappings */
gboolean strict_focus;
GHashTable *headers; /* holds user defined header appended to requests */
- char *contentsecuritypolicy; /* holds user defined Content-Security-Policy */
+ GSList *autoresponseheader; /* holds user defined list of auto-response-header */
char *nextpattern; /* regex patter nfor prev link matching */
char *prevpattern; /* regex patter nfor next link matching */
char *file; /* path to the custome config file */
#ifdef FEATURE_HSTS
#include "hsts.h"
#endif
+#include "arh.h"
typedef enum {
TYPE_BOOLEAN,
static int proxy(const char *name, int type, void *value, void *data);
static int user_style(const char *name, int type, void *value, void *data);
static int headers(const char *name, int type, void *value, void *data);
+static int autoresponseheader(const char *name, int type, void *value, void *data);
static int prevnext(const char *name, int type, void *value, void *data);
static int fullscreen(const char *name, int type, void *value, void *data);
#ifdef FEATURE_HSTS
setting_add("history-max-items", TYPE_INTEGER, &i, internal, 0, &vb.config.history_max);
setting_add("editor-command", TYPE_CHAR, &"x-terminal-emulator -e -vi '%s'", NULL, 0, NULL);
setting_add("header", TYPE_CHAR, &"", headers, FLAG_LIST|FLAG_NODUP, NULL);
- setting_add("content-security-policy", TYPE_CHAR, &"", internal, 0, &vb.config.contentsecuritypolicy);
+ setting_add("auto-response-header", TYPE_CHAR, &"", autoresponseheader, FLAG_LIST|FLAG_NODUP, NULL);
setting_add("nextpattern", TYPE_CHAR, &"/\\bnext\\b/i,/^(>\\|>>\\|»)$/,/^(>\\|>>\\|»)/,/(>\\|>>\\|»)$/,/\\bmore\\b/i", prevnext, FLAG_LIST|FLAG_NODUP, NULL);
setting_add("previouspattern", TYPE_CHAR, &"/\\bprev\\|previous\\b/i,/^(<\\|<<\\|«)$/,/^(<\\|<<\\|«)/,/(<\\|<<\\|«)$/", prevnext, FLAG_LIST|FLAG_NODUP, NULL);
setting_add("fullscreen", TYPE_BOOLEAN, &off, fullscreen, 0, NULL);
return SETTING_OK;
}
+static int autoresponseheader(const char *name, int type, void *value, void *data)
+{
+ const char *error = NULL;
+
+ GSList *new = arh_parse((char *)value, &error);
+
+ if (! error) {
+ /* remove previous parsed headers */
+ arh_free(vb.config.autoresponseheader);
+
+ /* add the new one */
+ vb.config.autoresponseheader = new;
+
+ return SETTING_OK;
+
+ } else {
+ vb_echo(VB_MSG_ERROR, true, "auto-response-header: %s", error);
+ return SETTING_ERROR | SETTING_USER_NOTIFIED;
+ }
+}
+
static int prevnext(const char *name, int type, void *value, void *data)
{
if (validate_js_regexp_list((char*)value)) {