Sandbox uriparser

This commit is contained in:
Mariusz Zaborski 2022-02-24 05:24:56 -05:00
parent 2d324bd50d
commit 74c7f66dee
12 changed files with 1231 additions and 0 deletions

View File

@ -20,6 +20,7 @@ set(SAPI_CONTRIB_SANDBOXES
libidn2
pffft
turbojpeg
uriparser
zopfli
zstd
)

View File

@ -13,6 +13,7 @@ Directory | Project
`libidn2/` | libidn2 - GNU IDN library | [www.gnu.org/software/libidn/#libidn2](https://www.gnu.org/software/libidn/#libidn2) | CMake
`pffft/` | PFFFT - a pretty fast Fourier Transform | [bitbucket.org/jpommier/pffft.git](https://bitbucket.org/jpommier/pffft.git) | CMake
`turbojpeg/` | High-level JPEG library | [libjpeg-turbo.org/About/TurboJPEG](https://libjpeg-turbo.org/About/TurboJPEG) | CMake
`uriparser/` | uriparser - Strictly RFC 3986 compliant URI parsing and handling library | [github.com/uriparser/uriparser](https://github.com/uriparser/uriparser.git) | CMake
`zopfli` | Zopfli - Compression Algorithm | [github.com/google/zopfli](https://github.com/google/zopfli) | CMake
`zstd/` | Zstandard - Fast real-time compression algorithm | [github.com/facebook/zstd](https://github.com/facebook/zstd) | CMake

View File

@ -0,0 +1,83 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
cmake_minimum_required(VERSION 3.13..3.22)
project(sapi_uriparser CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
if(NOT TARGET sapi::sapi)
set(SAPI_ROOT "../.." CACHE PATH "Path to the Sandboxed API source tree")
add_subdirectory("${SAPI_ROOT}"
"${CMAKE_BINARY_DIR}/sandboxed-api-build"
EXCLUDE_FROM_ALL)
endif()
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(HIDE_SYMBOLS OFF CACHE BOOL "" FORCE)
set(URIPARSER_BUILD_DOCS OFF CACHE BOOL "" FORCE)
set(URIPARSER_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(URIPARSER_BUILD_TOOLS OFF CACHE BOOL "" FORCE)
FetchContent_Declare(uriparser
GIT_REPOSITORY https://github.com/uriparser/uriparser
GIT_TAG e0dc98b767aadb86c6510c1b25e575084eeb803c
PATCH_COMMAND patch < "${CMAKE_SOURCE_DIR}/patches/uriparser.cmake.patch"
)
FetchContent_MakeAvailable(uriparser)
configure_file(uri.gen.h.in uri.gen.h)
add_sapi_library(
sapi_uriparser
FUNCTIONS
uriParseUriA
uriEscapeA
uriAddBaseUriA
uriRemoveBaseUriA
uriToStringA
uriToStringCharsRequiredA
uriNormalizeSyntaxMaskRequiredA
uriNormalizeSyntaxExA
uriDissectQueryMallocA
uriFreeQueryListA
uriFreeUriMembersA
INPUTS
"${CMAKE_BINARY_DIR}/uri.gen.h"
LIBRARY uriparser
LIBRARY_NAME Uriparser
NAMESPACE ""
)
add_library(sapi_contrib::uriparser ALIAS sapi_uriparser)
target_include_directories(sapi_uriparser INTERFACE
"${PROJECT_BINARY_DIR}"
"${SAPI_SOURCE_DIR}"
)
if(SAPI_ENABLE_EXAMPLES)
add_subdirectory(example)
endif()
if(SAPI_ENABLE_TESTS)
add_subdirectory(test)
endif()

View File

@ -0,0 +1,28 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
add_executable(
sapi_miniuriparser
main.cc
../utils/utils_uriparser.cc
)
target_link_libraries(
sapi_miniuriparser PRIVATE
sapi_uriparser
sapi::sapi
absl::flags_parse
)

View File

@ -0,0 +1,116 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include "absl/flags/parse.h"
#include "contrib/uriparser/sandboxed.h"
#include "contrib/uriparser/utils/utils_uriparser.h"
void Print(const char* name, absl::StatusOr<std::string> r) {
if (!r.ok()) {
std::cerr << "Unable to fetch " << name << "\n";
std::cerr << r.status() << "\n";
return;
}
if (r.value().empty()) {
return;
}
std::cout << name << ": " << *r << "\n";
}
int main(int argc, char* argv[]) {
std::string prog_name(argv[0]);
google::InitGoogleLogging(argv[0]);
std::vector<char*> args = absl::ParseCommandLine(argc, argv);
if (args.size() < 2) {
std::cerr << "Usage:\n " << prog_name << " URI ...\n";
return EXIT_FAILURE;
}
UriparserSapiSandbox sandbox;
if (!sandbox.Init().ok()) {
std::cerr << "Unable to start sandbox\n";
return EXIT_FAILURE;
}
int retval = EXIT_SUCCESS;
for (int i = 1; i < args.size(); ++i) {
UriParser uri(&sandbox, args[i]);
if (!uri.GetStatus().ok()) {
std::cerr << "Unable to parse: " << args[i] << "\n";
std::cerr << uri.GetStatus() << "\n";
retval = EXIT_FAILURE;
continue;
}
Print("scheme", uri.GetScheme());
Print("user info", uri.GetUserInfo());
Print("host", uri.GetHostText());
Print("host IP", uri.GetHostIP());
Print("port", uri.GetPortText());
Print("query", uri.GetQuery());
Print("fragment", uri.GetFragment());
absl::StatusOr<std::vector<std::string>> path = uri.GetPath();
if (!path.ok()) {
std::cerr << "Unable to get path.\n";
std::cerr << path.status() << "\n";
retval = EXIT_FAILURE;
continue;
}
if (!path->empty()) {
std::cout << "pathSeq: \n";
for (const auto& s : path.value()) {
std::cout << " - " << s << "\n";
}
}
absl::StatusOr<absl::btree_map<std::string, std::string>> query_map;
query_map = uri.GetQueryElements();
if (!query_map.ok()) {
std::cerr << "Unable to get query.\n";
std::cerr << query_map.status() << "\n";
retval = EXIT_FAILURE;
continue;
}
if (!query_map->empty()) {
std::cout << "Query elements: \n";
for (const auto& mp : query_map.value()) {
std::cout << " - " << mp.first << ": " << mp.second << "\n";
}
}
if (!uri.NormalizeSyntax().ok()) {
std::cerr << "Unable to normalize: " << args[i] << "\n";
continue;
}
absl::StatusOr<std::string> newuris = uri.GetUri();
if (!newuris.ok()) {
std::cerr << "Unable to reconstruct path.\n";
std::cerr << newuris.status() << "\n";
retval = EXIT_FAILURE;
continue;
}
std::cout << "Normalized path: " << newuris.value() << "\n";
}
return retval;
}

View File

@ -0,0 +1,23 @@
--- CMakeLists.txt
+++ CMakeLists.txt
@@ -59,6 +59,7 @@ include(GNUInstallDirs)
# Configuration
#
option(BUILD_SHARED_LIBS "Build shared libraries (rather than static ones)" ON)
+option(HIDE_SYMBOLS "Build a libraries with hidden symbols unless they're specifically exported" ON)
option(URIPARSER_BUILD_DOCS "Build API documentation (requires Doxygen, Graphviz, and (optional) Qt's qhelpgenerator)" ON)
option(URIPARSER_BUILD_TESTS "Build test suite (requires GTest >=1.8.0)" ON)
option(URIPARSER_BUILD_TOOLS "Build tools (e.g. CLI \"uriparse\")" ON)
@@ -104,10 +105,12 @@ endmacro()
#
set(URIPARSER_EXTRA_COMPILE_FLAGS)
+if(HIDE_SYMBOLS)
check_c_compiler_flag("-fvisibility=hidden" URIPARSER_COMPILER_SUPPORTS_VISIBILITY)
if(URIPARSER_COMPILER_SUPPORTS_VISIBILITY)
set(URIPARSER_EXTRA_COMPILE_FLAGS "${URIPARSER_EXTRA_COMPILE_FLAGS} -fvisibility=hidden")
endif()
+endif(HIDE_SYMBOLS)
#
# config.h

View File

@ -0,0 +1,39 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CONTRIB_URIPARSER_SANDBOXED_H_
#define CONTRIB_URIPARSER_SANDBOXED_H_
#include <libgen.h>
#include <syscall.h>
#include <memory>
#include "sapi_uriparser.sapi.h" // NOLINT(build/include)
class UriparserSapiSandbox : public UriparserSandbox {
public:
std::unique_ptr<sandbox2::Policy> ModifyPolicy(
sandbox2::PolicyBuilder*) override {
return sandbox2::PolicyBuilder()
.AllowDynamicStartup()
.AllowRead()
.AllowWrite()
.AllowSystemMalloc()
.AllowExit()
.BuildOrDie();
}
};
#endif // CONTRIB_URIPARSER_SANDBOXED_H_

View File

@ -0,0 +1,32 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
include(GoogleTest)
add_executable(
sapi_uriparser_test
test_uriparser.cc
../utils/utils_uriparser.cc
)
target_link_libraries(
sapi_uriparser_test PRIVATE
sapi_uriparser
sapi::test_main
)
gtest_discover_tests(sapi_uriparser_test)

View File

@ -0,0 +1,535 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <fstream>
#include "contrib/uriparser/sandboxed.h"
#include "contrib/uriparser/utils/utils_uriparser.h"
#include "sandboxed_api/util/path.h"
#include "sandboxed_api/util/status_matchers.h"
#include "sandboxed_api/util/temp_file.h"
namespace {
using ::sapi::IsOk;
const struct TestVariant {
std::string test;
std::string uri;
std::string uriescaped;
std::string scheme;
std::string userinfo;
std::string hosttext;
std::string hostip;
std::string porttext;
std::string query;
std::string fragment;
std::string normalized;
std::string add_base_example;
std::string remove_base_example;
std::vector<std::string> path_elements;
std::map<std::string, std::string> query_elements;
} TestData[] = {
{
.test = "http://www.example.com/",
.uri = "http://www.example.com/",
.uriescaped = "http%3A%2F%2Fwww.example.com%2F",
.scheme = "http",
.userinfo = "",
.hosttext = "www.example.com",
.hostip = "",
.porttext = "",
.query = "",
.fragment = "",
.normalized = "http://www.example.com/",
.add_base_example = "http://www.example.com/",
.remove_base_example = "./",
},
{
.test = "https://github.com/google/sandboxed-api/",
.uri = "https://github.com/google/sandboxed-api/",
.uriescaped = "https%3A%2F%2Fgithub.com%2Fgoogle%2Fsandboxed-api%2F",
.scheme = "https",
.userinfo = "",
.hosttext = "github.com",
.hostip = "",
.porttext = "",
.query = "",
.fragment = "",
.normalized = "https://github.com/google/sandboxed-api/",
.add_base_example = "https://github.com/google/sandboxed-api/",
.remove_base_example = "https://github.com/google/sandboxed-api/",
.path_elements = {
"google",
"sandboxed-api"
},
},
{
.test = "mailto:test@example.com",
.uri = "mailto:test@example.com",
.uriescaped = "mailto%3Atest%40example.com",
.scheme = "mailto",
.userinfo = "",
.hosttext = "",
.hostip = "",
.porttext = "",
.query = "",
.fragment = "",
.normalized = "mailto:test@example.com",
.add_base_example = "mailto:test@example.com",
.remove_base_example = "mailto:test@example.com",
.path_elements = {"test@example.com"},
},
{
.test = "file:///bin/bash",
.uri = "file:///bin/bash",
.uriescaped = "file%3A%2F%2F%2Fbin%2Fbash",
.scheme = "file",
.userinfo = "",
.hosttext = "",
.hostip = "",
.porttext = "",
.query = "",
.fragment = "",
.normalized = "file:///bin/bash",
.add_base_example = "file:///bin/bash",
.remove_base_example = "file:///bin/bash",
.path_elements = {
"bin",
"bash",
},
},
{
.test = "http://www.example.com/name%20with%20spaces/",
.uri = "http://www.example.com/name%20with%20spaces/",
.uriescaped =
"http%3A%2F%2Fwww.example.com%2Fname%2520with%2520spaces%2F",
.scheme = "http",
.userinfo = "",
.hosttext = "www.example.com",
.hostip = "",
.porttext = "",
.query = "",
.fragment = "",
.normalized = "http://www.example.com/name%20with%20spaces/",
.add_base_example = "http://www.example.com/name%20with%20spaces/",
.remove_base_example = "name%20with%20spaces/",
.path_elements = {
"name%20with%20spaces",
},
},
{
.test = "http://abcdefg@localhost/",
.uri = "http://abcdefg@localhost/",
.uriescaped = "http%3A%2F%2Fabcdefg%40localhost%2F",
.scheme = "http",
.userinfo = "abcdefg",
.hosttext = "localhost",
.hostip = "",
.porttext = "",
.query = "",
.fragment = "",
.normalized = "http://abcdefg@localhost/",
.add_base_example = "http://abcdefg@localhost/",
.remove_base_example = "//abcdefg@localhost/",
},
{
.test = "https://localhost:123/",
.uri = "https://localhost:123/",
.uriescaped = "https%3A%2F%2Flocalhost%3A123%2F",
.scheme = "https",
.userinfo = "",
.hosttext = "localhost",
.hostip = "",
.porttext = "123",
.query = "",
.fragment = "",
.normalized = "https://localhost:123/",
.add_base_example = "https://localhost:123/",
.remove_base_example = "https://localhost:123/",
},
{
.test = "http://[::1]/",
.uri = "http://[0000:0000:0000:0000:0000:0000:0000:0001]/",
.uriescaped = "http%3A%2F%2F%5B0000%3A0000%3A0000%3A0000%3A0000%3A0000%"
"3A0000%3A0001%5D%2F",
.scheme = "http",
.userinfo = "",
.hosttext = "::1",
.hostip = "::1",
.porttext = "",
.query = "",
.fragment = "",
.normalized = "http://[0000:0000:0000:0000:0000:0000:0000:0001]/",
.add_base_example = "http://[0000:0000:0000:0000:0000:0000:0000:0001]/",
.remove_base_example = "//[0000:0000:0000:0000:0000:0000:0000:0001]/",
},
{
.test = "http://a/b/c/d;p?q",
.uri = "http://a/b/c/d;p?q",
.uriescaped = "http%3A%2F%2Fa%2Fb%2Fc%2Fd%3Bp%3Fq",
.scheme = "http",
.userinfo = "",
.hosttext = "a",
.hostip = "",
.porttext = "",
.query = "q",
.fragment = "",
.normalized = "http://a/b/c/d;p?q",
.add_base_example = "http://a/b/c/d;p?q",
.remove_base_example = "//a/b/c/d;p?q",
.path_elements = {
"b",
"c",
"d;p"
},
.query_elements = {
{"q", ""}
},
},
{
.test = "http://a/b/c/../d;p?q",
.uri = "http://a/b/c/../d;p?q",
.uriescaped = "http%3A%2F%2Fa%2Fb%2Fc%2F..%2Fd%3Bp%3Fq",
.scheme = "http",
.userinfo = "",
.hosttext = "a",
.hostip = "",
.porttext = "",
.query = "q",
.fragment = "",
.normalized = "http://a/b/d;p?q",
.add_base_example = "http://a/b/d;p?q",
.remove_base_example = "//a/b/c/../d;p?q",
.path_elements = {
"b",
"c",
"..",
"d;p"
},
.query_elements = {
{"q", ""}
}
},
{
.test = "http://example.com/abc/def/",
.uri = "http://example.com/abc/def/",
.uriescaped = "http%3A%2F%2Fexample.com%2Fabc%2Fdef%2F",
.scheme = "http",
.userinfo = "",
.hosttext = "example.com",
.hostip = "",
.porttext = "",
.query = "",
.fragment = "",
.normalized = "http://example.com/abc/def/",
.add_base_example = "http://example.com/abc/def/",
.remove_base_example = "//example.com/abc/def/",
.path_elements = {
"abc",
"def",
},
},
{
.test = "http://example.com/?abc",
.uri = "http://example.com/?abc",
.uriescaped = "http%3A%2F%2Fexample.com%2F%3Fabc",
.scheme = "http",
.userinfo = "",
.hosttext = "example.com",
.hostip = "",
.porttext = "",
.query = "abc",
.fragment = "",
.normalized = "http://example.com/?abc",
.add_base_example = "http://example.com/?abc",
.remove_base_example = "//example.com/?abc",
.query_elements = {
{"abc", ""}
}
},
{
.test = "http://[vA.123456]/",
.uri = "http://[vA.123456]/",
.uriescaped = "http%3A%2F%2F%5BvA.123456%5D%2F",
.scheme = "http",
.userinfo = "",
.hosttext = "vA.123456",
.hostip = "",
.porttext = "",
.query = "",
.fragment = "",
.normalized = "http://[va.123456]/",
.add_base_example = "http://[vA.123456]/",
.remove_base_example = "//[vA.123456]/",
},
{
.test = "http://8.8.8.8/",
.uri = "http://8.8.8.8/",
.uriescaped = "http%3A%2F%2F8.8.8.8%2F",
.scheme = "http",
.userinfo = "",
.hosttext = "8.8.8.8",
.hostip = "8.8.8.8",
.porttext = "",
.query = "",
.fragment = "",
.normalized = "http://8.8.8.8/",
.add_base_example = "http://8.8.8.8/",
.remove_base_example = "//8.8.8.8/",
},
{
.test = "http://www.example.com/?abc",
.uri = "http://www.example.com/?abc",
.uriescaped = "http%3A%2F%2Fwww.example.com%2F%3Fabc",
.scheme = "http",
.userinfo = "",
.hosttext = "www.example.com",
.hostip = "",
.porttext = "",
.query = "abc",
.fragment = "",
.normalized = "http://www.example.com/?abc",
.add_base_example = "http://www.example.com/?abc",
.remove_base_example = "./?abc",
.query_elements = {
{"abc", ""}
}
},
{
.test = "https://google.com?q=asd&x=y&zxc=asd",
.uri = "https://google.com?q=asd&x=y&zxc=asd",
.uriescaped = "https%3A%2F%2Fgoogle.com%3Fq%3Dasd%26x%3Dy%26zxc%3Dasd",
.scheme = "https",
.userinfo = "",
.hosttext = "google.com",
.hostip = "",
.porttext = "",
.query = "q=asd&x=y&zxc=asd",
.fragment = "",
.normalized = "https://google.com?q=asd&x=y&zxc=asd",
.add_base_example = "https://google.com?q=asd&x=y&zxc=asd",
.remove_base_example = "https://google.com?q=asd&x=y&zxc=asd",
.query_elements = {
{"q", "asd"},
{"x", "y"},
{"zxc", "asd"}
}
},
{
.test = "https://google.com?q=asd#newplace",
.uri = "https://google.com?q=asd#newplace",
.uriescaped = "https%3A%2F%2Fgoogle.com%3Fq%3Dasd%23newplace",
.scheme = "https",
.userinfo = "",
.hosttext = "google.com",
.hostip = "",
.porttext = "",
.query = "q=asd",
.fragment = "newplace",
.normalized = "https://google.com?q=asd#newplace",
.add_base_example = "https://google.com?q=asd#newplace",
.remove_base_example = "https://google.com?q=asd#newplace",
.query_elements = {
{"q", "asd"}
}
},
};
class UriParserBase : public testing::Test {
protected:
void SetUp() override;
std::unique_ptr<UriparserSapiSandbox> sandbox_;
};
class UriParserTestData : public UriParserBase,
public testing::WithParamInterface<TestVariant> {};
void UriParserBase::SetUp() {
sandbox_ = std::make_unique<UriparserSapiSandbox>();
ASSERT_THAT(sandbox_->Init(), IsOk());
}
TEST_P(UriParserTestData, TestUri) {
const TestVariant& tv = GetParam();
UriParser uri(sandbox_.get(), tv.test);
ASSERT_THAT(uri.GetStatus(), IsOk());
SAPI_ASSERT_OK_AND_ASSIGN(std::string ret, uri.GetUri());
ASSERT_EQ(ret, tv.uri);
}
TEST_P(UriParserTestData, TestUriEscaped) {
const TestVariant& tv = GetParam();
UriParser uri(sandbox_.get(), tv.test);
ASSERT_THAT(uri.GetStatus(), IsOk());
SAPI_ASSERT_OK_AND_ASSIGN(std::string ret, uri.GetUriEscaped(true, true));
ASSERT_EQ(ret, tv.uriescaped);
}
TEST_P(UriParserTestData, TestScheme) {
const TestVariant& tv = GetParam();
UriParser uri(sandbox_.get(), tv.test);
ASSERT_THAT(uri.GetStatus(), IsOk());
SAPI_ASSERT_OK_AND_ASSIGN(std::string ret, uri.GetScheme());
ASSERT_EQ(ret, tv.scheme);
}
TEST_P(UriParserTestData, TestUserInfo) {
const TestVariant& tv = GetParam();
UriParser uri(sandbox_.get(), tv.test);
ASSERT_THAT(uri.GetStatus(), IsOk());
SAPI_ASSERT_OK_AND_ASSIGN(std::string ret, uri.GetUserInfo());
ASSERT_EQ(ret, tv.userinfo);
}
TEST_P(UriParserTestData, TestHostText) {
const TestVariant& tv = GetParam();
UriParser uri(sandbox_.get(), tv.test);
ASSERT_THAT(uri.GetStatus(), IsOk());
SAPI_ASSERT_OK_AND_ASSIGN(std::string ret, uri.GetHostText());
ASSERT_EQ(ret, tv.hosttext);
}
TEST_P(UriParserTestData, TestHostIP) {
const TestVariant& tv = GetParam();
UriParser uri(sandbox_.get(), tv.test);
ASSERT_THAT(uri.GetStatus(), IsOk());
SAPI_ASSERT_OK_AND_ASSIGN(std::string ret, uri.GetHostIP());
ASSERT_EQ(ret, tv.hostip);
}
TEST_P(UriParserTestData, TestPortText) {
const TestVariant& tv = GetParam();
UriParser uri(sandbox_.get(), tv.test);
ASSERT_THAT(uri.GetStatus(), IsOk());
SAPI_ASSERT_OK_AND_ASSIGN(std::string ret, uri.GetPortText());
ASSERT_EQ(ret, tv.porttext);
}
TEST_P(UriParserTestData, TestQuery) {
const TestVariant& tv = GetParam();
UriParser uri(sandbox_.get(), tv.test);
ASSERT_THAT(uri.GetStatus(), IsOk());
SAPI_ASSERT_OK_AND_ASSIGN(std::string ret, uri.GetQuery());
ASSERT_EQ(ret, tv.query);
}
TEST_P(UriParserTestData, TestFragment) {
const TestVariant& tv = GetParam();
UriParser uri(sandbox_.get(), tv.test);
ASSERT_THAT(uri.GetStatus(), IsOk());
SAPI_ASSERT_OK_AND_ASSIGN(std::string ret, uri.GetFragment());
ASSERT_EQ(ret, tv.fragment);
}
TEST_P(UriParserTestData, TestNormalize) {
const TestVariant& tv = GetParam();
UriParser uri(sandbox_.get(), tv.test);
ASSERT_THAT(uri.GetStatus(), IsOk());
ASSERT_THAT(uri.NormalizeSyntax(), IsOk());
SAPI_ASSERT_OK_AND_ASSIGN(std::string ret, uri.GetUri());
ASSERT_EQ(ret, tv.normalized);
}
TEST_P(UriParserTestData, TestMultiple) {
const TestVariant& tv = GetParam();
UriParser uri(sandbox_.get(), tv.test);
ASSERT_THAT(uri.GetStatus(), IsOk());
std::string ret;
SAPI_ASSERT_OK_AND_ASSIGN(ret, uri.GetQuery());
ASSERT_EQ(ret, tv.query);
SAPI_ASSERT_OK_AND_ASSIGN(ret, uri.GetHostIP());
ASSERT_EQ(ret, tv.hostip);
ASSERT_THAT(uri.NormalizeSyntax(), IsOk());
SAPI_ASSERT_OK_AND_ASSIGN(ret, uri.GetUri());
ASSERT_EQ(ret, tv.normalized);
}
TEST_P(UriParserTestData, TestAddBaseExample) {
const TestVariant& tv = GetParam();
UriParser uri(sandbox_.get(), tv.test);
ASSERT_THAT(uri.GetStatus(), IsOk());
SAPI_ASSERT_OK_AND_ASSIGN(std::string ret,
uri.GetUriWithBase("http://www.example.com"));
ASSERT_EQ(ret, tv.add_base_example);
}
TEST_P(UriParserTestData, TestRemoveBaseExample) {
const TestVariant& tv = GetParam();
UriParser uri(sandbox_.get(), tv.test);
ASSERT_THAT(uri.GetStatus(), IsOk());
SAPI_ASSERT_OK_AND_ASSIGN(
std::string ret, uri.GetUriWithoutBase("http://www.example.com", false));
ASSERT_EQ(ret, tv.remove_base_example);
}
TEST_P(UriParserTestData, TestPath) {
const TestVariant& tv = GetParam();
UriParser uri(sandbox_.get(), tv.test);
ASSERT_THAT(uri.GetStatus(), IsOk());
SAPI_ASSERT_OK_AND_ASSIGN(std::vector<std::string> ret, uri.GetPath());
ASSERT_EQ(ret.size(), tv.path_elements.size());
for (int i = 0; i < ret.size(); ++i) {
ASSERT_EQ(ret[i], tv.path_elements[i]);
}
}
TEST_P(UriParserTestData, TestQueryElements) {
const TestVariant& tv = GetParam();
UriParser uri(sandbox_.get(), tv.test);
ASSERT_THAT(uri.GetStatus(), IsOk());
SAPI_ASSERT_OK_AND_ASSIGN(auto ret, uri.GetQueryElements());
ASSERT_EQ(ret.size(), tv.query_elements.size());
for (auto orig : tv.query_elements) {
ASSERT_NE(ret.find(orig.first), ret.end());
ASSERT_EQ(ret[orig.first], orig.second);
}
}
INSTANTIATE_TEST_SUITE_P(UriParserBase, UriParserTestData,
testing::ValuesIn(TestData));
} // namespace

View File

@ -0,0 +1,21 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// This header ensure that we are getting ascii structures.
// The generator dosen't handle well redefined macros like
// URI_TYPE.
#define URI_PASS_ANSI
#define URI_ENABLE_ANSI
#include "${uriparser_SOURCE_DIR}/include/uriparser/Uri.h"

View File

@ -0,0 +1,286 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "contrib/uriparser/utils/utils_uriparser.h"
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <fstream>
#include <iostream>
#include <string>
#include "absl/cleanup/cleanup.h"
UriParser::~UriParser() {
if (GetStatus().ok()) {
api_.uriFreeUriMembersA(uri_.PtrBefore()).IgnoreError();
}
};
absl::Status UriParser::GetStatus() { return status_; }
absl::Status UriParser::ParseUri() {
SAPI_RETURN_IF_ERROR(sandbox_->Allocate(&uri_, true));
sapi::v::Struct<UriParserStateA> state;
state.mutable_data()->uri = reinterpret_cast<UriUriA*>(uri_.GetRemote());
SAPI_ASSIGN_OR_RETURN(
int ret, api_.uriParseUriA(state.PtrBefore(), c_uri_.PtrBefore()));
if (ret != 0) {
return absl::UnavailableError("Unable to parse uri");
}
SAPI_RETURN_IF_ERROR(sandbox_->TransferFromSandboxee(&uri_));
return absl::OkStatus();
}
absl::StatusOr<std::string> UriParser::FetchUriText(UriTextRangeA* ptr) {
if (ptr == nullptr) {
return "";
}
if (ptr->first == nullptr) {
return "";
}
size_t size = ptr->afterLast - ptr->first;
// Sometimes uriparser dosen't allocate new memory
// and sometimes it does.
SAPI_ASSIGN_OR_RETURN(
std::string uri,
sandbox_->GetCString(sapi::v::RemotePtr(const_cast<char*>(ptr->first))));
return uri.substr(0, size);
}
absl::StatusOr<std::string> UriParser::GetScheme() {
return FetchUriText(&uri_.mutable_data()->scheme);
}
absl::StatusOr<std::string> UriParser::GetUserInfo() {
return FetchUriText(&uri_.mutable_data()->userInfo);
}
absl::StatusOr<std::string> UriParser::GetHostText() {
return FetchUriText(&uri_.mutable_data()->hostText);
}
absl::StatusOr<std::string> UriParser::GetHostIP() {
char ipstr[INET6_ADDRSTRLEN] = "";
UriHostDataA* data = &uri_.mutable_data()->hostData;
if (uri_.mutable_data()->hostData.ip4) {
sapi::v::Struct<UriIp4> ip4;
ip4.SetRemote(uri_.mutable_data()->hostData.ip4);
SAPI_RETURN_IF_ERROR(sandbox_->TransferFromSandboxee(&ip4));
inet_ntop(AF_INET, ip4.mutable_data()->data, ipstr, sizeof(ipstr));
} else if (uri_.mutable_data()->hostData.ip6) {
sapi::v::Struct<UriIp6> ip6;
ip6.SetRemote(uri_.mutable_data()->hostData.ip6);
SAPI_RETURN_IF_ERROR(sandbox_->TransferFromSandboxee(&ip6));
inet_ntop(AF_INET6, ip6.mutable_data()->data, ipstr, sizeof(ipstr));
}
return std::string(ipstr);
}
absl::StatusOr<std::string> UriParser::GetPortText() {
return FetchUriText(&uri_.mutable_data()->portText);
}
absl::StatusOr<std::string> UriParser::GetQuery() {
return FetchUriText(&uri_.mutable_data()->query);
}
absl::StatusOr<std::string> UriParser::GetFragment() {
return FetchUriText(&uri_.mutable_data()->fragment);
}
absl::StatusOr<std::vector<std::string>> UriParser::GetPath() {
std::vector<std::string> ret;
UriPathSegmentA* pathHead = uri_.mutable_data()->pathHead;
if (pathHead == nullptr) {
return ret;
}
sapi::v::Struct<UriPathSegmentA> path_segment;
path_segment.SetRemote(pathHead);
while (path_segment.GetRemote() != nullptr) {
SAPI_RETURN_IF_ERROR(sandbox_->TransferFromSandboxee(&path_segment));
SAPI_ASSIGN_OR_RETURN(std::string seg,
FetchUriText(&path_segment.mutable_data()->text));
if (!seg.empty()) {
ret.push_back(seg);
}
path_segment.SetRemote(path_segment.mutable_data()->next);
}
return ret;
}
absl::Status UriParser::NormalizeSyntax() {
SAPI_ASSIGN_OR_RETURN(int dirty_parts,
api_.uriNormalizeSyntaxMaskRequiredA(uri_.PtrNone()));
return NormalizeSyntax(dirty_parts);
}
absl::Status UriParser::NormalizeSyntax(int norm_mask) {
SAPI_ASSIGN_OR_RETURN(int ret,
api_.uriNormalizeSyntaxExA(uri_.PtrAfter(), norm_mask));
if (ret != 0) {
return absl::UnavailableError("Unable to normalize");
}
return absl::OkStatus();
}
absl::StatusOr<std::string> UriParser::GetUri() { return GetUri(&uri_); }
absl::StatusOr<std::string> UriParser::GetUri(sapi::v::Struct<UriUriA>* uri) {
sapi::v::Int size;
int ret;
SAPI_ASSIGN_OR_RETURN(
ret, api_.uriToStringCharsRequiredA(uri->PtrNone(), size.PtrAfter()));
if (ret != 0) {
return absl::UnavailableError("Unable to get size");
}
sapi::v::Array<char> buf(size.GetValue() + 1);
sapi::v::NullPtr null_ptr;
SAPI_ASSIGN_OR_RETURN(ret, api_.uriToStringA(buf.PtrAfter(), uri->PtrNone(),
buf.GetSize(), &null_ptr));
if (ret != 0) {
return absl::UnavailableError("Unable to Recomposing URI");
}
return std::string(buf.GetData());
}
absl::StatusOr<std::string> UriParser::GetUriEscaped(bool space_to_plus,
bool normalize_breaks) {
SAPI_ASSIGN_OR_RETURN(std::string uri, GetUri());
// Be sure to allocate *6 times* the space of the input buffer for
// *6 times* for _normalizeBreaks == URI_TRUE_
int space = uri.length() * 6 + 1;
sapi::v::Array<char> bufout(space);
sapi::v::ConstCStr bufin(uri.c_str());
SAPI_RETURN_IF_ERROR(api_.uriEscapeA(bufin.PtrBefore(), bufout.PtrAfter(),
space_to_plus, normalize_breaks));
return std::string(bufout.GetData());
}
absl::StatusOr<std::string> UriParser::GetUriWithBase(const std::string& base) {
UriParser base_uri(sandbox_, base);
sapi::v::Struct<UriUriA> newuri;
SAPI_ASSIGN_OR_RETURN(int ret,
api_.uriAddBaseUriA(newuri.PtrAfter(), uri_.PtrNone(),
base_uri.uri_.PtrBefore()));
if (ret != 0) {
return absl::UnavailableError("Unable to add base");
}
absl::Cleanup newuri_cleanup = [this, &newuri] {
api_.uriFreeUriMembersA(newuri.PtrNone()).IgnoreError();
};
return GetUri(&newuri);
}
absl::StatusOr<std::string> UriParser::GetUriWithoutBase(
const std::string& base, bool domain_root_mode) {
UriParser base_uri(sandbox_, base);
sapi::v::Struct<UriUriA> newuri;
SAPI_ASSIGN_OR_RETURN(
int ret,
api_.uriRemoveBaseUriA(newuri.PtrAfter(), uri_.PtrNone(),
base_uri.uri_.PtrBefore(), domain_root_mode));
if (ret != 0) {
return absl::UnavailableError("Unable to remove base");
}
absl::Cleanup newuri_cleanup = [this, &newuri] {
api_.uriFreeUriMembersA(newuri.PtrNone()).IgnoreError();
};
return GetUri(&newuri);
}
absl::StatusOr<absl::btree_map<std::string, std::string>>
UriParser::GetQueryElements() {
absl::btree_map<std::string, std::string> outquery;
if (uri_.mutable_data()->query.first == nullptr) {
return outquery;
}
sapi::v::Array<void*> query_ptr(1);
sapi::v::Int query_count;
sapi::v::RemotePtr first(const_cast<char*>(uri_.mutable_data()->query.first));
sapi::v::RemotePtr afterLast(
const_cast<char*>(uri_.mutable_data()->query.afterLast));
SAPI_ASSIGN_OR_RETURN(
int ret,
api_.uriDissectQueryMallocA(query_ptr.PtrAfter(), query_count.PtrAfter(),
&first, &afterLast));
if (ret != 0) {
return absl::UnavailableError("Unable to get query list");
}
absl::Cleanup query_list_cleanup = [this, &query_ptr] {
sapi::v::RemotePtr rptr(query_ptr[0]);
api_.uriFreeQueryListA(&rptr).IgnoreError();
};
sapi::v::Struct<UriQueryListA> obj;
obj.SetRemote(query_ptr[0]);
for (int i = 0; i < query_count.GetValue(); ++i) {
SAPI_RETURN_IF_ERROR(sandbox_->TransferFromSandboxee(&obj));
obj.SetRemote(nullptr);
void* key_p = const_cast<char*>(obj.mutable_data()->key);
void* value_p = const_cast<char*>(obj.mutable_data()->value);
SAPI_ASSIGN_OR_RETURN(std::string key_s,
sandbox_->GetCString(sapi::v::RemotePtr(key_p)));
std::string value_s;
if (value_p != nullptr) {
SAPI_ASSIGN_OR_RETURN(value_s,
sandbox_->GetCString(sapi::v::RemotePtr(value_p)));
} else {
value_s = "";
}
outquery[key_s] = value_s;
obj.SetRemote(obj.mutable_data()->next);
}
obj.SetRemote(nullptr);
return outquery;
}

View File

@ -0,0 +1,66 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CONTRIB_URIPARSER_UTILS_UTILS_ZIP_H_
#define CONTRIB_URIPARSER_UTILS_UTILS_ZIP_H_
#include <vector>
#include "absl/container/btree_map.h"
#include "contrib/uriparser/sandboxed.h"
#include "sandboxed_api/util/status_macros.h"
class UriParser {
public:
UriParser(UriparserSandbox* sandbox, const std::string& uri)
: sandbox_(CHECK_NOTNULL(sandbox)), api_(sandbox_), c_uri_(uri.c_str()) {
status_ = ParseUri();
};
~UriParser();
absl::Status GetStatus();
absl::Status NormalizeSyntax();
absl::Status NormalizeSyntax(int norm_mask);
absl::StatusOr<std::string> GetUri();
absl::StatusOr<std::string> GetUriWithBase(const std::string& base);
absl::StatusOr<std::string> GetUriWithoutBase(const std::string& base,
bool domain_root_mode);
absl::StatusOr<std::string> GetScheme();
absl::StatusOr<std::string> GetUserInfo();
absl::StatusOr<std::string> GetHostText();
absl::StatusOr<std::string> GetHostIP();
absl::StatusOr<std::string> GetPortText();
absl::StatusOr<std::string> GetQuery();
absl::StatusOr<std::string> GetFragment();
absl::StatusOr<std::string> GetUriEscaped(bool space_to_plus,
bool normalize_breaks);
absl::StatusOr<std::vector<std::string>> GetPath();
absl::StatusOr<absl::btree_map<std::string, std::string>> GetQueryElements();
protected:
absl::StatusOr<std::string> FetchUriText(UriTextRangeA* ptr);
absl::StatusOr<std::string> GetUri(sapi::v::Struct<UriUriA>* uri);
absl::Status ParseUri();
sapi::v::Struct<UriUriA> uri_;
private:
UriparserSandbox* sandbox_;
UriparserApi api_;
sapi::v::ConstCStr c_uri_; // We have to keep a orginal string in sandbox
absl::Status status_;
};
#endif // CONTRIB_URIARSER_UTILS_UTILS_ZIP_H_