From d1108b181738db74e231ec84067e174b95e42dcb Mon Sep 17 00:00:00 2001 From: Mariusz Zaborski Date: Sat, 26 Mar 2022 11:14:20 -0400 Subject: [PATCH] Sandbox libxls --- contrib/CMakeLists.txt | 1 + contrib/README.md | 1 + contrib/libxls/CMakeLists.txt | 117 +++++++++++++++++++ contrib/libxls/example/CMakeLists.txt | 28 +++++ contrib/libxls/example/main.cc | 89 ++++++++++++++ contrib/libxls/files/t1.xls | Bin 0 -> 5632 bytes contrib/libxls/files/t2.xls | Bin 0 -> 6144 bytes contrib/libxls/files/t3.xls | Bin 0 -> 5632 bytes contrib/libxls/sandboxed.h | 48 ++++++++ contrib/libxls/test/CMakeLists.txt | 33 ++++++ contrib/libxls/test/libxls_test.cc | 143 +++++++++++++++++++++++ contrib/libxls/utils/utils_libxls.cc | 162 ++++++++++++++++++++++++++ contrib/libxls/utils/utils_libxls.h | 106 +++++++++++++++++ contrib/libxls/xls.gen.h.in | 21 ++++ 14 files changed, 749 insertions(+) create mode 100644 contrib/libxls/CMakeLists.txt create mode 100644 contrib/libxls/example/CMakeLists.txt create mode 100644 contrib/libxls/example/main.cc create mode 100644 contrib/libxls/files/t1.xls create mode 100644 contrib/libxls/files/t2.xls create mode 100644 contrib/libxls/files/t3.xls create mode 100644 contrib/libxls/sandboxed.h create mode 100644 contrib/libxls/test/CMakeLists.txt create mode 100644 contrib/libxls/test/libxls_test.cc create mode 100644 contrib/libxls/utils/utils_libxls.cc create mode 100644 contrib/libxls/utils/utils_libxls.h create mode 100644 contrib/libxls/xls.gen.h.in diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index c2d802a78..81bc867ef 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -18,6 +18,7 @@ set(SAPI_CONTRIB_SANDBOXES hunspell jsonnet libidn2 + libxls libzip pffft turbojpeg diff --git a/contrib/README.md b/contrib/README.md index 7f8060d5b..d184de215 100644 --- a/contrib/README.md +++ b/contrib/README.md @@ -11,6 +11,7 @@ Directory | Project `hunspell/` | Hunspell - The most popular spellchecking library | [github.com/hunspell/hunspell](https://github.com/hunspell/hunspell) | CMake `jsonnet/` | Jsonnet - The Data Templating Language | [github.com/google/jsonnet](https://github.com/google/jsonnet) | CMake `libidn2/` | libidn2 - GNU IDN library | [www.gnu.org/software/libidn/#libidn2](https://www.gnu.org/software/libidn/#libidn2) | CMake +`libxls/` | libxls- Read binary Excel files from C/C++ | [https://github.com/libxls/libxls](https://github.com/libxls/libxls) | CMake `libzip/` | libzip - operations on zip archives | [github.com/nih-at/libzip](https://github.com/nih-at/libzip) | 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 diff --git a/contrib/libxls/CMakeLists.txt b/contrib/libxls/CMakeLists.txt new file mode 100644 index 000000000..b5c07f53d --- /dev/null +++ b/contrib/libxls/CMakeLists.txt @@ -0,0 +1,117 @@ +# 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_libxls) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +set(SAPI_ROOT "" CACHE PATH "Path to the Sandboxed API source tree") + +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() + +FetchContent_Declare(libxls + GIT_REPOSITORY https://github.com/libxls/libxls.git + GIT_TAG 448240067919707eb95fb009f76f3fdb439b1427 +) + +FetchContent_GetProperties(libxls) +if(NOT libxls_POPULATED) + FetchContent_Populate(libxls) + set(libxls_STATUS_FILE "${libxls_SOURCE_DIR}/config.status") + if(EXISTS "${libxls_STATUS_FILE}") + file(SHA256 "${libxls_STATUS_FILE}" _sapi_CONFIG_STATUS) + endif() + if(NOT _sapi_CONFIG_STATUS STREQUAL "${libxls_CONFIG_STATUS}") + message("-- Configuring libxls...") + execute_process( + COMMAND autoreconf -i + WORKING_DIRECTORY "${libxls_SOURCE_DIR}" + RESULT_VARIABLE _sapi_libxls_autoreconf_result + ) + if(NOT _sapi_libxls_autoreconf_result EQUAL "0") + message(FATAL_ERROR "Configuration for libxls failed: " + "${_sapi_libxls_autoreconf_result}") + endif() + execute_process( + COMMAND ./configure --disable-dependency-tracking + WORKING_DIRECTORY "${libxls_SOURCE_DIR}" + RESULT_VARIABLE _sapi_libxls_config_result + ) + if(NOT _sapi_libxls_config_result EQUAL "0") + message(FATAL_ERROR "Configuration for libxls failed: " + "${_sapi_libxls_config_result}") + endif() + file(SHA256 "${libxls_SOURCE_DIR}/config.status" _sapi_CONFIG_STATUS) + set(libxls_CONFIG_STATUS "${_sapi_CONFIG_STATUS}" CACHE INTERNAL "") + endif() +endif() + +add_library(libxls STATIC + "${libxls_SOURCE_DIR}/src/endian.c" + "${libxls_SOURCE_DIR}/src/locale.c" + "${libxls_SOURCE_DIR}/src/ole.c" + "${libxls_SOURCE_DIR}/src/xls.c" + "${libxls_SOURCE_DIR}/src/xlstool.c" +) + +target_include_directories(libxls PUBLIC + "${libxls_SOURCE_DIR}" + "${libxls_SOURCE_DIR}/include" +) + +configure_file(xls.gen.h.in xls.gen.h) + +add_sapi_library( + sapi_libxls + + FUNCTIONS + xls_open_file + + xls_getWorkSheet + xls_parseWorkSheet + + xls_cell + + xls_close_WS + xls_close_WB + + xls_getError + + INPUTS + "${CMAKE_BINARY_DIR}/xls.gen.h" + + LIBRARY libxls + LIBRARY_NAME Libxls + NAMESPACE "" +) +add_library(sapi_contrib::libxls ALIAS sapi_libxls) +target_include_directories(sapi_libxls INTERFACE + "${PROJECT_BINARY_DIR}" +) + +if(SAPI_ENABLE_EXAMPLES) + add_subdirectory(example) +endif() + +if(SAPI_ENABLE_TESTS) + add_subdirectory(test) +endif() diff --git a/contrib/libxls/example/CMakeLists.txt b/contrib/libxls/example/CMakeLists.txt new file mode 100644 index 000000000..3882c393d --- /dev/null +++ b/contrib/libxls/example/CMakeLists.txt @@ -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_minixls + + main.cc + ../utils/utils_libxls.cc +) + +target_link_libraries( + sapi_minixls PRIVATE + + sapi_libxls + sapi::sapi + absl::flags_parse +) diff --git a/contrib/libxls/example/main.cc b/contrib/libxls/example/main.cc new file mode 100644 index 000000000..c219edc7b --- /dev/null +++ b/contrib/libxls/example/main.cc @@ -0,0 +1,89 @@ +// 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 +#include + +#include +#include +#include +#include +#include + +#include "absl/flags/flag.h" +#include "absl/flags/parse.h" +#include "contrib/libxls/sandboxed.h" +#include "contrib/libxls/utils/utils_libxls.h" + +ABSL_FLAG(uint32_t, sheet, 0, "sheet number"); + +int main(int argc, char* argv[]) { + std::string prog_name(argv[0]); + google::InitGoogleLogging(argv[0]); + std::vector args = absl::ParseCommandLine(argc, argv); + + if (args.size() != 2) { + std::cerr << "Usage:\n " << prog_name << " INPUT\n"; + return EXIT_FAILURE; + } + + LibxlsSapiSandbox sandbox(args[1]); + if (!sandbox.Init().ok()) { + std::cerr << "Unable to start sandbox\n"; + return EXIT_FAILURE; + } + + absl::StatusOr wb = LibXlsWorkbook::Open(&sandbox, args[1]); + + uint32_t nr_sheet = absl::GetFlag(FLAGS_sheet); + + absl::StatusOr sheet = wb->OpenSheet(nr_sheet); + if (!sheet.ok()) { + std::cerr << "Unable to switch sheet: "; + std::cerr << sheet.status() << "\n"; + return EXIT_FAILURE; + } + + for (size_t row = 0; row < sheet->GetRowCount(); ++row) { + for (size_t col = 0; col < sheet->GetColCount(); ++col) { + absl::StatusOr cell = sheet->GetCell(row, col); + if (!cell.ok()) { + std::cerr << "Unable to get cell: "; + std::cerr << cell.status() << "\n"; + return EXIT_FAILURE; + } + switch (cell->type) { + case XLS_RECORD_NUMBER: + std::cout << std::setw(16) << std::get(cell->value) << " | "; + break; + case XLS_RECORD_STRING: + std::cout << std::setw(16) << std::get(cell->value) + << " | "; + break; + case XLS_RECORD_BOOL: + std::cout << std::setw(16) << std::get(cell->value) << " | "; + break; + case XLS_RECORD_BLANK: + std::cout << std::setw(16) << " | "; + break; + case XLS_RECORD_ERROR: + std::cout << "error" + << "\n"; + break; + } + } + std::cout << "\n"; + } + return EXIT_SUCCESS; +} diff --git a/contrib/libxls/files/t1.xls b/contrib/libxls/files/t1.xls new file mode 100644 index 0000000000000000000000000000000000000000..ac94180499c5a15ba9cbf34534606638a076c11f GIT binary patch literal 5632 zcmeHLU2GIp6h1S%Tc$sBx1~h|sl%#EOIu^*L5#sJ6jTzCT52?gM7P^%scc(P7Su%J zQsluzjD`naNR*^6MxziDHBn>x=7UCKLQ;Jyz7dUC491W^*Y7(swZpPYcTI!A?A@Gu z?|gIaoO91P_uPB;hwmHaPMukEPV&lw5|H^^wN&|V1AS}ZIU-`BPi#Jy%Pqj@R?1Ce zfh(S_^kEQA41wtL6(C~OUV9TwsiJBb7U+|4{L*5}4jDr`Auqw>rI*!+7{9nuM*8zb zDf~^y zi(i24l>>~8o9-pbx0VD&1nSb7W51`3AA25R`g4)w`lZ%DK7SAG(3@9G~%lJ zajm0T*P}M%Gad7@B3+2fZ@`~>^(}7^gp94oe1BEY9aprFFpmE+MJ$O7VO;a+98{dw-v!V zir^cI;MLdQCrqrVjO5rxG68KHGF93(W`f$T%B;|~DHF;)r?Gns@&5!9HCF1LeU6jv zdIM|Iz;DK|KqCFe4f$1`VTR83>2#{H%*0&6j047$pEgNtmKpqOob4?+Vj$yN(^x@b z#iiu}b3Agsh1_o;r(1s^?I*2fN&a&YaC1J`#N%}j8kj%e3xo7CM@F54_T$bAcHj7D zVyvUj89tOsOhjW{_rll>Tq~(0~24BAw{iB_;lby5K z?lxcllteMLK@fkF(qVKM3*#4BL!QL9EqO)vQ88YxD9u(zKx23(Zi4C^i)*1g+B`PHzrXdPAF z-=vpL%|41JI5f+UQ03sgumrOADsM2`s(5wQofRH@#grNy$LQqU;~}$AKGjQS+{d+! z-!nNW2w-cP!wAP=Ul1a;xLKQZ8>QjQvP+sM$6yY0AR%C2EAS7_NI-2wBMF-?+R=!dyu@BVRV_rAyn zZ(Gv7;gg@}kC#C-FW*yfp6}uy+Uzk9&v{RSc-9*P@w|5cL<#4wi29%XE>IjKhfk|p zwa;giIOy=3pTBoGKFd5JzuWd1_R+nj6w{wF%(LH}J$cXC$ccBLf5)YdwgU_0VS9lw zyYb^BD#NkVwHS`qmEu`oIq!dJ|GYI+$k5Cr8hB`)!tHZH_RE0YL-!YAzb@7YZ`0pM zAG(IG%j|nOt2u@-(*wz)i1FRf;dviNXqeo*Kw^ zCA)P&raX{2oOd;rknE8|nExnFy}bMH#J>PT>JuVqoQB7N6fdoweCF;_=YI!yKlDBs llI@U?R2>}s2ckTEHvfzHMJa)-~YAzzX9|IC9?nk literal 0 HcmV?d00001 diff --git a/contrib/libxls/files/t2.xls b/contrib/libxls/files/t2.xls new file mode 100644 index 0000000000000000000000000000000000000000..35f6d6608313f49b2e72131d5327138f2d3884ec GIT binary patch literal 6144 zcmeHLU1(fI6h3#eo8J61yGfI(t&O*9v-ua3J{XajZPTg+YeO1Qq@bJRCXL-BB~ASS z#cgUI1S^$3_!7`UUW&yZ1Qj%xy!oIgLLvHM>RZrLMB0O`eUdj~CRUDZ4-&?O`Ijfo}uWEkzJyo%ki^k+y_{|b|4Jg4^Xcfw`IQ${d)I*quVa6biKD07AP{Yi-iIC zYU6hd`5OB#!y`WN&!}Jg2HIgcM&Ia&#e1!PtL#x7Vg`M48cw>)@Z+xGlL??qK34kI zTJKW-16x$AHt_IHvS38PCvryXu-0X$4f#Un{GyZ=c;>g@O1*KG(=Z`m>_!9}l$gXN zV-yT)HCpX2l}TpU$L(QzG!~!JlQSuQr7TEEmuZW1DyH;>{ODYDHvU|6J~bQv|Cc@q zn_66+ThexJbnKAKq}S)5H|3x=P6k9w7!k=YT!3xn1D$48AE=T9!BltbGpF7q{S9j zJVt(c2SINm(*2!Ml4NxM28^bhG>{FE*@I zEzAcjJ|+<-*o1BhHl+~O2gP_e%T;g)qOM)I^3#Ey&i3b(o}iv?siD!JUnR5h66IBDH|unkOrt9KtB>a_cyzvwDy_wmxE z+@u=h8No({Q5$fa3Y`|zE1csp#!W6`d0hrOxnh^WoJE(>kQRmHbWxXKprA57x`(lh z%cN)KYz^Ex3*cPzvye#>;l81@JjOfgfx+Iex%W7fV`dhNLN@I%eru^u}fxAkD{ z8SL+GbJ$l`V=a!*)lG-?jx92$(?)Zr-~aRK!6Ts$-!-MN_OqXPc3%T%SB_J0-tVIT z&)1Ux@A=OHyzBP?yzd_asO0>0=kCsaD-Z>wZGzW&r_T;b6nN>KFF&{zon@|&h555n z?4x^2$)!JK_%3~aY2tycp>yv+|E8-S?FB86K|2f@-!u4dg7JuRscSAehS!U0fwg@9 zSNmt2p=^fcjYAy`%`^D=Fe*oJH{o}PquJPRORK#qgxMfZ^b*$WB1s)MaxpjM_&+bY)I U0NG=%{IzISYJczdm;YM+KV)`KUH||9 literal 0 HcmV?d00001 diff --git a/contrib/libxls/files/t3.xls b/contrib/libxls/files/t3.xls new file mode 100644 index 0000000000000000000000000000000000000000..a14cd649f9b5b8333e9cfcdf5278ad324538fdb8 GIT binary patch literal 5632 zcmeHLU1(fY5T3g~>D?qvHfd5dwedEz+oVavSiy?aY?@XLSR2!bB1PS7Zhq`0DcjT^ zP~4{WL9n9q!IwfURZu=tyHAaX8pdocg^j(o9s%cRx+D8 zXU?2CcjnBQGv}Oq_ifGmnX?V&B?s;mzbs@zQs&_spv`PMEaC%A-a;mm$+AJN!VTns ztFEnF!vGdB2;`ct0g@MT>lD?SPxte zya{+S@D?ETZE$PSji7IH+nd~aaR#f@_4Gi<#445r<@2>)Gv#aUKaYs`C@`yW@e_Cl z@*ii))Z6>lV$jHudvLdzyC zt56#9sgC(kIUUjZl!t~1C=t(^YsCH+)l<4^SW=Q z<+qFlEm@ec$fse-zf>Hb-pVImh|jO)lmGwnr{PnJ({mx~=T^rL>0EwG0e)Kn{Nm{94zf%CxRc1+-q5TBCJcDwug%WA`}X{}Co??9}}uwmsSL8uq4v z->hK*Mfy(|^0Pd_44v-N>6E3JiMfRt2aG8%ZkLKQGx(Rd+naLKK*l#`u!F>mO5;W5 zc;tK&x!*)iH-AUkkDK)p|LY;(Q?%GSwd*(4I_jx_a;j@gH;hwbOAzKOBecw3)6 zd?XP|Hbpw_jC9pYi$s={>x~WDR;2fsDtJZ92#ndTaG`Unhi(RatbJ~(eJIdOPmXwusAeB4f0_uSpy9~-a}2^%gE*)=*eAQeYpgei@9ez(~wgFM#v#)>l_Lb-y_pxjl`J>0*HK);l<~2~re(La@`{vxK z+c$(yz6JZ+E`P8aykHLR0C>zU{5X+e#j&)t5Fg#E@E`HOO1}Se{d4wkzG?EUk#e)e zV|aZg<)94cJM>^a@g;e+h&IL>GM46$C51hmHa88xkZx!m!yelQ8=i5PC6v_9`7nPG ziW%Afp&nPzfZl}I#SF_UQx5%(W~a<6l*7CgZNAkALD?@yF#j>!dinP6!G8gUbWO;a z#BF#2oWjuT>gRD$?EHC>z3+Z9D7&E{t~S{EABfWI+3^qNZ$bB@tA8b`QvL6JfB)P1 F{{*&4DJK8` literal 0 HcmV?d00001 diff --git a/contrib/libxls/sandboxed.h b/contrib/libxls/sandboxed.h new file mode 100644 index 000000000..bdc19bfe1 --- /dev/null +++ b/contrib/libxls/sandboxed.h @@ -0,0 +1,48 @@ +// 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_ZSTD_SANDBOXED_H_ +#define CONTRIB_ZSTD_SANDBOXED_H_ + +#include +#include + +#include + +#include "sapi_libxls.sapi.h" // NOLINT(build/include) + +class LibxlsSapiSandbox : public LibxlsSandbox { + public: + explicit LibxlsSapiSandbox(std::string filename) + : filename_(std::move(filename)) {} + + std::unique_ptr ModifyPolicy( + sandbox2::PolicyBuilder*) override { + return sandbox2::PolicyBuilder() + .AllowDynamicStartup() + .AllowOpen() + .AllowRead() + .AllowWrite() + .AllowSystemMalloc() + .AllowExit() + .AllowSyscall(__NR_recvmsg) + .AddFile(filename_) + .BuildOrDie(); + } + + private: + std::string filename_; +}; + +#endif // CONTRIB_LIBXLS_SANDBOXED_H_ diff --git a/contrib/libxls/test/CMakeLists.txt b/contrib/libxls/test/CMakeLists.txt new file mode 100644 index 000000000..0dc7c9888 --- /dev/null +++ b/contrib/libxls/test/CMakeLists.txt @@ -0,0 +1,33 @@ +# 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_libxls_test + + libxls_test.cc + ../utils/utils_libxls.cc +) + + +target_link_libraries( + sapi_libxls_test PRIVATE + + sapi_libxls + sapi::test_main + sapi::temp_file +) + +gtest_discover_tests(sapi_libxls_test PROPERTIES ENVIRONMENT "TEST_FILES_DIR=${PROJECT_SOURCE_DIR}/files") diff --git a/contrib/libxls/test/libxls_test.cc b/contrib/libxls/test/libxls_test.cc new file mode 100644 index 000000000..b7afb8769 --- /dev/null +++ b/contrib/libxls/test/libxls_test.cc @@ -0,0 +1,143 @@ +// 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/libxls/sandboxed.h" +#include "contrib/libxls/utils/utils_libxls.h" +#undef FILE // XXX: artefect from genereted header + +#include "sandboxed_api/util/path.h" +#include "sandboxed_api/util/status_matchers.h" + +namespace { + +using ::sapi::IsOk; +using ::testing::Not; + +struct Sheet { + int count_row; + int count_col; + double values[4][4]; +}; + +const struct TestCase { + std::string filename; + size_t sheet_count; + struct Sheet sheet[2]; +} kTestData[] = { + { + .filename = "t1.xls", + .sheet_count = 1, + .sheet = + { + {.count_row = 4, + .count_col = 2, + .values = + { + {1, 2, 0, 0}, + {3, 4, 0, 0}, + {5, 6, 0, 0}, + {7, 8, 0, 0}, + }}, + }, + }, + { + .filename = "t2.xls", + .sheet_count = 2, + .sheet = + { + {.count_row = 2, + .count_col = 3, + .values = + { + {1, 2, 3, 0}, + {4, 5, 6, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + }}, + {.count_row = 2, + .count_col = 2, + .values = + { + {9, 8, 0, 0}, + {7, 6, 0, 0}, + {0, 0, 0, 0}, + {0, 0, 0, 0}, + }}, + }, + }, +}; + +class LibXlsBase : public testing::Test { + protected: + std::string GetTestFilePath(const std::string& filename) { + return sapi::file::JoinPath(test_dir_, filename); + } + + void SetUp() override; + + const char* test_dir_; +}; + +class LibXlsTestFiles : public LibXlsBase, + public testing::WithParamInterface {}; + +void LibXlsBase::SetUp() { + test_dir_ = getenv("TEST_FILES_DIR"); + ASSERT_NE(test_dir_, nullptr); +} + +TEST_P(LibXlsTestFiles, TestValues) { + const TestCase& tv = GetParam(); + std::string test_file_path = GetTestFilePath(tv.filename); + + LibxlsSapiSandbox sandbox(test_file_path); + SAPI_ASSERT_OK(sandbox.Init()); + + SAPI_ASSERT_OK_AND_ASSIGN(LibXlsWorkbook wb, + LibXlsWorkbook::Open(&sandbox, test_file_path)); + ASSERT_EQ(wb.GetSheetCount(), tv.sheet_count); + + for (int i = 0; i < tv.sheet_count; ++i) { + SAPI_ASSERT_OK_AND_ASSIGN(LibXlsSheet sheet, wb.OpenSheet(i)); + ASSERT_EQ(sheet.GetRowCount(), tv.sheet[i].count_row); + ASSERT_EQ(sheet.GetColCount(), tv.sheet[i].count_col); + for (size_t row = 0; row < sheet.GetRowCount(); ++row) { + for (size_t col = 0; col < sheet.GetColCount(); ++col) { + SAPI_ASSERT_OK_AND_ASSIGN(LibXlsCell cell, sheet.GetCell(row, col)); + ASSERT_EQ(cell.type, XLS_RECORD_NUMBER); + ASSERT_EQ(std::get(cell.value), tv.sheet[i].values[row][col]); + } + } + } +} + +INSTANTIATE_TEST_SUITE_P(LibXlsBase, LibXlsTestFiles, + testing::ValuesIn(kTestData)); + +TEST_F(LibXlsBase, TestFormula) { + std::string test_file_path = GetTestFilePath("t3.xls"); + + LibxlsSapiSandbox sandbox(test_file_path); + SAPI_ASSERT_OK(sandbox.Init()); + + SAPI_ASSERT_OK_AND_ASSIGN(LibXlsWorkbook wb, + LibXlsWorkbook::Open(&sandbox, test_file_path)); + + SAPI_ASSERT_OK_AND_ASSIGN(LibXlsSheet sheet, wb.OpenSheet(0)); + SAPI_ASSERT_OK_AND_ASSIGN(LibXlsCell cell, sheet.GetCell(0, 0)); + ASSERT_EQ(cell.type, XLS_RECORD_STRING); + ASSERT_EQ(std::get(cell.value), "10.000000"); +} + +} // namespace diff --git a/contrib/libxls/utils/utils_libxls.cc b/contrib/libxls/utils/utils_libxls.cc new file mode 100644 index 000000000..6efc3c148 --- /dev/null +++ b/contrib/libxls/utils/utils_libxls.cc @@ -0,0 +1,162 @@ +// 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/libxls/utils/utils_libxls.h" + +#include +#include +#include + +#include "contrib/libxls/sandboxed.h" + +absl::Status GetError(LibxlsApi* api, xls_error_t error_code) { + SAPI_ASSIGN_OR_RETURN(const char* c_errstr, api->xls_getError(error_code)); + sapi::v::RemotePtr sapi_errstr(const_cast(c_errstr)); + SAPI_ASSIGN_OR_RETURN(std::string errstr, + api->GetSandbox()->GetCString(sapi_errstr)); + + return absl::UnavailableError(errstr); +} + +absl::StatusOr LibXlsWorkbook::Open(LibxlsSapiSandbox* sandbox, + const std::string& filename, + const std::string& encode) { + if (sandbox == nullptr) { + return absl::InvalidArgumentError("Sandbox has to be defined"); + } + + LibxlsApi api(sandbox); + + sapi::v::IntBase sapi_error; + sapi::v::CStr sapi_filename(filename.c_str()); + sapi::v::CStr sapi_encode(encode.c_str()); + + SAPI_ASSIGN_OR_RETURN( + xlsWorkBook* wb, + api.xls_open_file(sapi_filename.PtrBefore(), sapi_encode.PtrBefore(), + sapi_error.PtrAfter())); + + if (wb == nullptr) { + return GetError(&api, sapi_error.GetValue()); + } + + sapi::v::Struct sapi_wb; + sapi_wb.SetRemote(wb); + SAPI_RETURN_IF_ERROR(sandbox->TransferFromSandboxee(&sapi_wb)); + + return LibXlsWorkbook(sandbox, wb, sapi_wb.data().sheets.count); +} + +LibXlsWorkbook::~LibXlsWorkbook() { + if (rwb_ != nullptr) { + sapi::v::RemotePtr sapi_rwb(rwb_); + LibxlsApi api(sandbox_); + api.xls_close_WB(&sapi_rwb).IgnoreError(); + } +} + +size_t LibXlsWorkbook::GetSheetCount() { return sheet_count_; } + +absl::StatusOr LibXlsWorkbook::OpenSheet(uint32_t index) { + if (GetSheetCount() <= index) { + return absl::OutOfRangeError("Index out of range"); + } + + LibxlsApi api(sandbox_); + sapi::v::RemotePtr sapi_rwb(rwb_); + SAPI_ASSIGN_OR_RETURN(xlsWorkSheet* ws, + api.xls_getWorkSheet(&sapi_rwb, index)); + if (ws == nullptr) { + return absl::UnavailableError("Unable to open sheet"); + } + + sapi::v::Struct sapi_ws; + sapi_ws.SetRemote(ws); + SAPI_ASSIGN_OR_RETURN(xls_error_t error_code, + api.xls_parseWorkSheet(sapi_ws.PtrAfter())); + if (error_code != 0) { + return GetError(&api, error_code); + } + + return LibXlsSheet(sandbox_, ws, sapi_ws.data().rows.lastrow + 1, + sapi_ws.data().rows.lastcol + 1); +} + +size_t LibXlsSheet::GetRowCount() const { return row_; } + +size_t LibXlsSheet::GetColCount() const { return col_; } + +absl::StatusOr LibXlsSheet::GetStr( + const sapi::v::Struct& sapi_cell) { + if (sapi_cell.data().str == nullptr) { + return ""; + } + + sapi::v::RemotePtr sapi_str(sapi_cell.data().str); + return sandbox_->GetCString(sapi_str); +} + +absl::StatusOr LibXlsSheet::GetNewCell( + const sapi::v::Struct& sapi_cell) { + int id = sapi_cell.data().id; + double d = sapi_cell.data().d; + + switch (id) { + case XLS_RECORD_RK: + case XLS_RECORD_MULRK: + case XLS_RECORD_NUMBER: + return LibXlsCell{XLS_RECORD_NUMBER, d}; + case XLS_RECORD_BLANK: + return LibXlsCell{XLS_RECORD_BLANK, 0.0}; + case XLS_RECORD_FORMULA: + SAPI_ASSIGN_OR_RETURN(std::string cell_str, GetStr(sapi_cell)); + if (cell_str == "bool") { + return LibXlsCell{XLS_RECORD_BOOL, d > 0}; + } else if (cell_str == "error") { + return LibXlsCell{XLS_RECORD_ERROR, cell_str}; + } + return LibXlsCell{XLS_RECORD_STRING, cell_str}; + } + + return absl::UnavailableError("Unknown type"); +} + +absl::StatusOr LibXlsSheet::GetCell(uint32_t row, uint32_t col) { + if (row >= GetRowCount()) { + return absl::OutOfRangeError("Row out of range"); + } + if (col >= GetColCount()) { + return absl::OutOfRangeError("Col out of range"); + } + + LibxlsApi api(sandbox_); + sapi::v::RemotePtr sapi_rws(rws_); + SAPI_ASSIGN_OR_RETURN(xlsCell* cell, api.xls_cell(&sapi_rws, row, col)); + if (cell == nullptr) { + return absl::UnavailableError("Unable to get cell"); + } + sapi::v::Struct sapi_cell; + sapi_cell.SetRemote(cell); + SAPI_RETURN_IF_ERROR(sandbox_->TransferFromSandboxee(&sapi_cell)); + + return GetNewCell(sapi_cell); +} + +LibXlsSheet::~LibXlsSheet() { + if (rws_ != nullptr) { + LibxlsApi api(sandbox_); + sapi::v::RemotePtr sapi_rws(rws_); + api.xls_close_WS(&sapi_rws).IgnoreError(); + } +} diff --git a/contrib/libxls/utils/utils_libxls.h b/contrib/libxls/utils/utils_libxls.h new file mode 100644 index 000000000..6b0d77f30 --- /dev/null +++ b/contrib/libxls/utils/utils_libxls.h @@ -0,0 +1,106 @@ +// 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_LIBPCAP_UTILS_UTILS_LIBPCAP_H_ +#define CONTRIB_LIBPCAP_UTILS_UTILS_LIBPCAP_H_ + +#include + +#include "contrib/libxls/sandboxed.h" + +#define XLS_RECORD_FORMULA 0x0006 +#define XLS_RECORD_MULRK 0x00BD +#define XLS_RECORD_BLANK 0x0201 +#define XLS_RECORD_NUMBER 0x0203 +#define XLS_RECORD_STRING 0x0207 +#define XLS_RECORD_RK 0x027E +#define XLS_RECORD_BOOL 0x9998 +#define XLS_RECORD_ERROR 0x9999 + +struct LibXlsCell { + int type; + std::variant value; +}; + +class LibXlsSheet { + friend class LibXlsWorkbook; + + public: + size_t GetRowCount() const; + size_t GetColCount() const; + absl::StatusOr GetCell(uint32_t row, uint32_t col); + + ~LibXlsSheet(); + + LibXlsSheet(LibXlsSheet&& other) { *this = std::move(other); } + + LibXlsSheet& operator=(LibXlsSheet&& other) { + if (this != &other) { + std::swap(sandbox_, other.sandbox_); + std::swap(rws_, other.rws_); + std::swap(row_, other.row_); + std::swap(col_, other.col_); + } + return *this; + } + + private: + LibXlsSheet(LibxlsSapiSandbox* sandbox, xlsWorkSheet* rws, size_t row, + size_t col) + : sandbox_(CHECK_NOTNULL(sandbox)), rws_(rws), row_(row), col_(col) {} + + absl::StatusOr GetStr(const sapi::v::Struct& sapi_cell); + absl::StatusOr GetNewCell( + const sapi::v::Struct& sapi_cell); + + LibxlsSapiSandbox* sandbox_; + xlsWorkSheet* rws_ = nullptr; + size_t row_; + size_t col_; +}; + +class LibXlsWorkbook { + public: + static absl::StatusOr Open( + LibxlsSapiSandbox* sandbox, const std::string& filename, + const std::string& encode = "UTF-8"); + + size_t GetSheetCount(); + absl::StatusOr OpenSheet(uint32_t index); + + ~LibXlsWorkbook(); + + LibXlsWorkbook(LibXlsWorkbook&& other) { *this = std::move(other); } + + LibXlsWorkbook& operator=(LibXlsWorkbook&& other) { + if (this != &other) { + std::swap(sandbox_, other.sandbox_); + std::swap(rwb_, other.rwb_); + std::swap(sheet_count_, other.sheet_count_); + } + return *this; + } + + private: + LibXlsWorkbook(LibxlsSapiSandbox* sandbox, xlsWorkBook* rwb, size_t count) + : sandbox_(CHECK_NOTNULL(sandbox)), + rwb_(CHECK_NOTNULL(rwb)), + sheet_count_(count) {} + + LibxlsSapiSandbox* sandbox_; + xlsWorkBook* rwb_ = nullptr; + size_t sheet_count_; +}; + +#endif // CONTRIB_LIBPCAP_UTILS_UTILS_LIBPCAP_H_ diff --git a/contrib/libxls/xls.gen.h.in b/contrib/libxls/xls.gen.h.in new file mode 100644 index 000000000..8ab4980d9 --- /dev/null +++ b/contrib/libxls/xls.gen.h.in @@ -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. + +#define FILE void +#undef __cplusplus +extern "C" { +#include "${libxls_SOURCE_DIR}/include/xls.h" +} +#define __cplusplus +#undef FILE