def write(self, *vals): ctx = context.force_epsilon_transition(self.ctx_) underlying = self.underlying_ for val in vals: ctx_after, esc_modes, problem = (escaping.esc_mode_for_hole(ctx)) if problem is not None: raise AutoescapeError(problem) ctx = ctx_after for esc_mode in esc_modes: escaper = escaping.SANITIZER_FOR_ESC_MODE[esc_mode] val = escaper(val) underlying.write(val) self.ctx_ = ctx
def write(self, *vals): ctx = context.force_epsilon_transition(self.ctx_) underlying = self.underlying_ for val in vals: ctx_after, esc_modes, problem = ( escaping.esc_mode_for_hole(ctx) ) if problem is not None: raise AutoescapeError(problem) ctx = ctx_after for esc_mode in esc_modes: escaper = escaping.SANITIZER_FOR_ESC_MODE[esc_mode] val = escaper(val) underlying.write(val) self.ctx_ = ctx
def esc_mode_for_hole(context_before): """ Given a context in which an untrusted value hole appears, computes the escaping modes needed to render that untrusted value safe for interpolation and the context after the hole. context_before - The input context before the substitution. Returns (context after, (escaping_modes...,)) """ ctx = context.force_epsilon_transition(context_before) state, url_part = context.state_of(ctx), context.url_part_of(ctx) esc_modes = [ESC_MODE_FOR_STATE[state]] problem = None if url_part == context.URL_PART_NONE: # Make sure that at the start of a URL, we filter out dangerous # protocols. if state in ( context.STATE_URL, context.STATE_CSS_URL, context.STATE_CSSDQ_URL, context.STATE_CSSSQ_URL): esc_modes = [ESC_MODE_FILTER_URL, ESC_MODE_NORMALIZE_URL] ctx = (ctx & ~context.URL_PART_ALL) | context.URL_PART_PRE_QUERY elif state in (context.STATE_CSSDQ_STR, context.STATE_CSSSQ_STR): esc_modes[:0] = [ESC_MODE_FILTER_URL] ctx = (ctx & ~context.URL_PART_ALL) | context.URL_PART_PRE_QUERY elif url_part == context.URL_PART_PRE_QUERY: if state not in (context.STATE_CSSDQ_STR, context.STATE_CSSSQ_STR): esc_modes[0] = ESC_MODE_NORMALIZE_URL elif url_part == context.URL_PART_QUERY_OR_FRAG: esc_modes[0] = ESC_MODE_ESCAPE_URL elif url_part == context.URL_PART_UNKNOWN: ctx = context.STATE_ERROR problem = 'hole appears in an ambiguous URL context' if state == context.STATE_JS: ctx = (ctx & ~context.JS_CTX_ALL) | context.JS_CTX_DIV_OP elif (state == context.STATE_ATTR_NAME and context.attr_type_of(ctx) != context.ATTR_NONE): esc_modes[0] = ESC_MODE_FILTER_HTML_ATTR_SUFFIX if esc_modes[0] is None: ctx = context.STATE_ERROR esc_mode = esc_modes[-1] delim_type = context.delim_type_of(ctx) if delim_type != context.DELIM_NONE: # Figure out how to escape the attribute value. if esc_mode != ESC_MODE_ESCAPE_HTML_ATTRIBUTE: esc_modes.append(ESC_MODE_ESCAPE_HTML_ATTRIBUTE) if (context.delim_type_of(context_before) == context.DELIM_NONE and delim_type == context.DELIM_SPACE_OR_TAG_END): esc_modes.append(ESC_MODE_OPEN_QUOTE) last, i = esc_modes[0], 1 while i < len(esc_modes): curr = esc_modes[i] # If, for all x, f(g(x)) == g(x), we can skip f. if (last, curr) in REDUNDANT_ESC_MODES: esc_modes[i:i+1] = [] else: last = curr i += 1 return ctx, tuple(esc_modes), problem
def esc_mode_for_hole(context_before): """ Given a context in which an untrusted value hole appears, computes the escaping modes needed to render that untrusted value safe for interpolation and the context after the hole. context_before - The input context before the substitution. Returns (context after, (escaping_modes...,)) """ ctx = context.force_epsilon_transition(context_before) state, url_part = context.state_of(ctx), context.url_part_of(ctx) esc_modes = [ESC_MODE_FOR_STATE[state]] problem = None if url_part == context.URL_PART_NONE: # Make sure that at the start of a URL, we filter out dangerous # protocols. if state in (context.STATE_URL, context.STATE_CSS_URL, context.STATE_CSSDQ_URL, context.STATE_CSSSQ_URL): esc_modes = [ESC_MODE_FILTER_URL, ESC_MODE_NORMALIZE_URL] ctx = (ctx & ~context.URL_PART_ALL) | context.URL_PART_PRE_QUERY elif state in (context.STATE_CSSDQ_STR, context.STATE_CSSSQ_STR): esc_modes[:0] = [ESC_MODE_FILTER_URL] ctx = (ctx & ~context.URL_PART_ALL) | context.URL_PART_PRE_QUERY elif url_part == context.URL_PART_PRE_QUERY: if state not in (context.STATE_CSSDQ_STR, context.STATE_CSSSQ_STR): esc_modes[0] = ESC_MODE_NORMALIZE_URL elif url_part == context.URL_PART_QUERY_OR_FRAG: esc_modes[0] = ESC_MODE_ESCAPE_URL elif url_part == context.URL_PART_UNKNOWN: ctx = context.STATE_ERROR problem = 'hole appears in an ambiguous URL context' if state == context.STATE_JS: ctx = (ctx & ~context.JS_CTX_ALL) | context.JS_CTX_DIV_OP elif (state == context.STATE_ATTR_NAME and context.attr_type_of(ctx) != context.ATTR_NONE): esc_modes[0] = ESC_MODE_FILTER_HTML_ATTR_SUFFIX if esc_modes[0] is None: ctx = context.STATE_ERROR esc_mode = esc_modes[-1] delim_type = context.delim_type_of(ctx) if delim_type != context.DELIM_NONE: # Figure out how to escape the attribute value. if esc_mode != ESC_MODE_ESCAPE_HTML_ATTRIBUTE: esc_modes.append(ESC_MODE_ESCAPE_HTML_ATTRIBUTE) if (context.delim_type_of(context_before) == context.DELIM_NONE and delim_type == context.DELIM_SPACE_OR_TAG_END): esc_modes.append(ESC_MODE_OPEN_QUOTE) last, i = esc_modes[0], 1 while i < len(esc_modes): curr = esc_modes[i] # If, for all x, f(g(x)) == g(x), we can skip f. if (last, curr) in REDUNDANT_ESC_MODES: esc_modes[i:i + 1] = [] else: last = curr i += 1 return ctx, tuple(esc_modes), problem