tests/testACLMaxUserIP.cc
endif
+## Tests of html/*
+
+check_PROGRAMS += tests/testHtmlQuote
+tests_testHtmlQuote_SOURCES = \
+ tests/testHtmlQuote.cc
+nodist_tests_testHtmlQuote_SOURCES = \
+ $(TESTSOURCES) \
+ tests/stub_debug.cc \
+ tests/stub_libmem.cc
+tests_testHtmlQuote_LDADD= \
+ html/libhtml.la \
+ sbuf/libsbuf.la \
+ base/libbase.la \
+ $(LIBCPPUNIT_LIBS) \
+ $(COMPAT_LIB) \
+ $(XTRA_LIBS)
+tests_testHtmlQuote_LDFLAGS = $(LIBADD_DL)
+
## Tests of http/* and HTTP Protocol objects
check_PROGRAMS += tests/test_http_range
#include "squid.h"
#include "html/Quoting.h"
+#include "sbuf/SBuf.h"
-/*
- * HTML defines these characters as special entities that should be quoted.
- */
-static struct {
- unsigned char code;
- const char *quote;
-} htmlstandardentities[] =
+#include <array>
+#include <cstring>
+static const auto &
+EscapeSequences()
{
- /* NOTE: The quoted form MUST not be larger than 6 character.
- * see close to the MemPool commend below
- */
- {
- '<', "<"
- },
- {
- '>', ">"
- },
- {
- '"', """
- },
- {
- '&', "&"
- },
- {
- '\'', "'"
- },
- {
- 0, NULL
+ static auto escapeMap = new std::array<SBuf, 256> {};
+ auto &em = *escapeMap;
+ if (!em['<'].isEmpty())
+ return em;
+
+ // Encode control chars just to be on the safe side and make sure all 8-bit
+ // characters are encoded to protect from buggy clients.
+ for (int ch = 0; ch < 256; ++ch) {
+ if ((ch <= 0x1F || ch >= 0x7f) && ch != '\n' && ch != '\r' && ch != '\t') {
+ em[ch] = SBuf().Printf("&#%d;", ch);
+ }
}
-};
-/*
- * html_do_quote - Returns a static buffer containing the quoted
- * string.
- */
+ em['<'] = "<";
+ em['>'] = ">";
+ em['"'] = """;
+ em['&'] = "&";
+ em['\''] = "'";
+
+ return em;
+}
+
char *
html_quote(const char *string)
{
+ static const auto &escapeSequences = EscapeSequences();
static char *buf = nullptr;
static size_t bufsize = 0;
const char *src;
char *dst;
- int i;
/* XXX This really should be implemented using a MemPool, but
* MemPools are not yet available in lib...
buf = static_cast<char *>(xcalloc(bufsize, 1));
}
for (src = string, dst = buf; *src; src++) {
- const char *escape = NULL;
const unsigned char ch = *src;
- /* Walk thru the list of HTML Entities that must be quoted to
- * display safely
- */
- for (i = 0; htmlstandardentities[i].code; i++) {
- if (ch == htmlstandardentities[i].code) {
- escape = htmlstandardentities[i].quote;
- break;
- }
- }
- /* Encode control chars just to be on the safe side, and make
- * sure all 8-bit characters are encoded to protect from buggy
- * clients
- */
- if (!escape && (ch <= 0x1F || ch >= 0x7f) && ch != '\n' && ch != '\r' && ch != '\t') {
- static char dec_encoded[7];
- snprintf(dec_encoded, sizeof dec_encoded, "&#%3d;", (int) ch);
- escape = dec_encoded;
- }
- if (escape) {
+ const auto &escape = escapeSequences[ch];
+ if (!escape.isEmpty()) {
/* Ok, An escaped form was found above. Use it */
- strncpy(dst, escape, 7);
- dst += strlen(escape);
+ escape.copy(dst, 7);
+ dst += escape.length();
} else {
/* Apparently there is no need to escape this character */
*dst++ = ch;
#ifndef SQUID__SRC_HTML_QUOTING_H
#define SQUID__SRC_HTML_QUOTING_H
+/** Obtain a static buffer containing an HTML-encoded version of the given c-string.
+ *
+ * HTML reserved characters are replaced with character references
+ * per https://html.spec.whatwg.org/#character-references
+ */
char *html_quote(const char *);
#endif /* SQUID__SRC_HTML_QUOTING_H */
--- /dev/null
+/*
+ * Copyright (C) 1996-2023 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#include "squid.h"
+#include "compat/cppunit.h"
+#include "html/Quoting.h"
+#include "unitTestMain.h"
+
+#include <cstring>
+#include <iostream>
+
+class TestHtmlQuote: public CPPUNIT_NS::TestFixture
+{
+ CPPUNIT_TEST_SUITE(TestHtmlQuote);
+ CPPUNIT_TEST(test_html_quote_cstr);
+ CPPUNIT_TEST_SUITE_END();
+
+protected:
+ void test_html_quote_cstr();
+ void testPerformance();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION( TestHtmlQuote );
+
+void
+TestHtmlQuote::test_html_quote_cstr()
+{
+ CPPUNIT_ASSERT_EQUAL(std::string(""), std::string(html_quote("")));
+ CPPUNIT_ASSERT_EQUAL(std::string("bar"), std::string(html_quote("bar")));
+ CPPUNIT_ASSERT_EQUAL(std::string("foo<bar>gazonk"), std::string(html_quote("foo<bar>gazonk")));
+ CPPUNIT_ASSERT_EQUAL(std::string("foo&bar"), std::string(html_quote("foo&bar")));
+ CPPUNIT_ASSERT_EQUAL(std::string("some'thing"), std::string(html_quote("some'thing")));
+ CPPUNIT_ASSERT_EQUAL(std::string("some"thing"), std::string(html_quote("some\"thing")));
+ CPPUNIT_ASSERT_EQUAL(std::string("<>"&'"), std::string(html_quote("<>\"&'")));
+ CPPUNIT_ASSERT_EQUAL(std::string(">"), std::string(html_quote(">")));
+ CPPUNIT_ASSERT_EQUAL(std::string("£"), std::string(html_quote("\xa3")));
+
+ for (unsigned char ch = 1; ch < 0xff; ++ch) {
+ unsigned char buf[2] = {ch, '\0'};
+ auto quoted = html_quote(reinterpret_cast<char *>(buf));
+
+ if (strlen(quoted) == 1) {
+ CPPUNIT_ASSERT_EQUAL(static_cast<int>(ch), static_cast<int>(quoted[0]));
+ } else {
+ CPPUNIT_ASSERT(strlen(quoted) >= 3);
+ CPPUNIT_ASSERT_EQUAL('&', quoted[0]);
+ CPPUNIT_ASSERT_EQUAL(';', quoted[strlen(quoted)-1]);
+ if (quoted[1] == '#') {
+ CPPUNIT_ASSERT(strlen(quoted) > 3);
+ CPPUNIT_ASSERT(strlen(quoted) <= 6);
+ }
+ }
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ return TestProgram().run(argc, argv);
+}