11cb0ef41Sopenharmony_ci#include "fs_permission.h"
21cb0ef41Sopenharmony_ci#include "base_object-inl.h"
31cb0ef41Sopenharmony_ci#include "debug_utils-inl.h"
41cb0ef41Sopenharmony_ci#include "util.h"
51cb0ef41Sopenharmony_ci#include "v8.h"
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ci#include <fcntl.h>
81cb0ef41Sopenharmony_ci#include <limits.h>
91cb0ef41Sopenharmony_ci#include <stdlib.h>
101cb0ef41Sopenharmony_ci#include <algorithm>
111cb0ef41Sopenharmony_ci#include <filesystem>
121cb0ef41Sopenharmony_ci#include <string>
131cb0ef41Sopenharmony_ci#include <string_view>
141cb0ef41Sopenharmony_ci#include <vector>
151cb0ef41Sopenharmony_ci
161cb0ef41Sopenharmony_cinamespace {
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_cistd::string WildcardIfDir(const std::string& res) noexcept {
191cb0ef41Sopenharmony_ci  uv_fs_t req;
201cb0ef41Sopenharmony_ci  int rc = uv_fs_stat(nullptr, &req, res.c_str(), nullptr);
211cb0ef41Sopenharmony_ci  if (rc == 0) {
221cb0ef41Sopenharmony_ci    const uv_stat_t* const s = static_cast<const uv_stat_t*>(req.ptr);
231cb0ef41Sopenharmony_ci    if (s->st_mode & S_IFDIR) {
241cb0ef41Sopenharmony_ci      // add wildcard when directory
251cb0ef41Sopenharmony_ci      if (res.back() == node::kPathSeparator) {
261cb0ef41Sopenharmony_ci        return res + "*";
271cb0ef41Sopenharmony_ci      }
281cb0ef41Sopenharmony_ci      return res + node::kPathSeparator + "*";
291cb0ef41Sopenharmony_ci    }
301cb0ef41Sopenharmony_ci  }
311cb0ef41Sopenharmony_ci  uv_fs_req_cleanup(&req);
321cb0ef41Sopenharmony_ci  return res;
331cb0ef41Sopenharmony_ci}
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_civoid FreeRecursivelyNode(
361cb0ef41Sopenharmony_ci    node::permission::FSPermission::RadixTree::Node* node) {
371cb0ef41Sopenharmony_ci  if (node == nullptr) {
381cb0ef41Sopenharmony_ci    return;
391cb0ef41Sopenharmony_ci  }
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ci  if (node->children.size()) {
421cb0ef41Sopenharmony_ci    for (auto& c : node->children) {
431cb0ef41Sopenharmony_ci      FreeRecursivelyNode(c.second);
441cb0ef41Sopenharmony_ci    }
451cb0ef41Sopenharmony_ci  }
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_ci  if (node->wildcard_child != nullptr) {
481cb0ef41Sopenharmony_ci    delete node->wildcard_child;
491cb0ef41Sopenharmony_ci  }
501cb0ef41Sopenharmony_ci  delete node;
511cb0ef41Sopenharmony_ci}
521cb0ef41Sopenharmony_ci
531cb0ef41Sopenharmony_cibool is_tree_granted(node::permission::FSPermission::RadixTree* granted_tree,
541cb0ef41Sopenharmony_ci                     const std::string_view& param) {
551cb0ef41Sopenharmony_ci#ifdef _WIN32
561cb0ef41Sopenharmony_ci  // is UNC file path
571cb0ef41Sopenharmony_ci  if (param.rfind("\\\\", 0) == 0) {
581cb0ef41Sopenharmony_ci    // return lookup with normalized param
591cb0ef41Sopenharmony_ci    int starting_pos = 4;  // "\\?\"
601cb0ef41Sopenharmony_ci    if (param.rfind("\\\\?\\UNC\\") == 0) {
611cb0ef41Sopenharmony_ci      starting_pos += 4;  // "UNC\"
621cb0ef41Sopenharmony_ci    }
631cb0ef41Sopenharmony_ci    auto normalized = param.substr(starting_pos);
641cb0ef41Sopenharmony_ci    return granted_tree->Lookup(normalized, true);
651cb0ef41Sopenharmony_ci  }
661cb0ef41Sopenharmony_ci#endif
671cb0ef41Sopenharmony_ci  return granted_tree->Lookup(param, true);
681cb0ef41Sopenharmony_ci}
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_ci}  // namespace
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_cinamespace node {
731cb0ef41Sopenharmony_ci
741cb0ef41Sopenharmony_cinamespace permission {
751cb0ef41Sopenharmony_ci
761cb0ef41Sopenharmony_civoid PrintTree(const FSPermission::RadixTree::Node* node, size_t spaces = 0) {
771cb0ef41Sopenharmony_ci  std::string whitespace(spaces, ' ');
781cb0ef41Sopenharmony_ci
791cb0ef41Sopenharmony_ci  if (node == nullptr) {
801cb0ef41Sopenharmony_ci    return;
811cb0ef41Sopenharmony_ci  }
821cb0ef41Sopenharmony_ci  if (node->wildcard_child != nullptr) {
831cb0ef41Sopenharmony_ci    per_process::Debug(DebugCategory::PERMISSION_MODEL,
841cb0ef41Sopenharmony_ci                       "%s Wildcard: %s\n",
851cb0ef41Sopenharmony_ci                       whitespace,
861cb0ef41Sopenharmony_ci                       node->prefix);
871cb0ef41Sopenharmony_ci  } else {
881cb0ef41Sopenharmony_ci    per_process::Debug(DebugCategory::PERMISSION_MODEL,
891cb0ef41Sopenharmony_ci                       "%s Prefix: %s\n",
901cb0ef41Sopenharmony_ci                       whitespace,
911cb0ef41Sopenharmony_ci                       node->prefix);
921cb0ef41Sopenharmony_ci    if (node->children.size()) {
931cb0ef41Sopenharmony_ci      size_t child = 0;
941cb0ef41Sopenharmony_ci      for (const auto& pair : node->children) {
951cb0ef41Sopenharmony_ci        ++child;
961cb0ef41Sopenharmony_ci        per_process::Debug(DebugCategory::PERMISSION_MODEL,
971cb0ef41Sopenharmony_ci                           "%s Child(%s): %s\n",
981cb0ef41Sopenharmony_ci                           whitespace,
991cb0ef41Sopenharmony_ci                           child,
1001cb0ef41Sopenharmony_ci                           std::string(1, pair.first));
1011cb0ef41Sopenharmony_ci        PrintTree(pair.second, spaces + 2);
1021cb0ef41Sopenharmony_ci      }
1031cb0ef41Sopenharmony_ci      per_process::Debug(DebugCategory::PERMISSION_MODEL,
1041cb0ef41Sopenharmony_ci                         "%s End of tree - child(%s)\n",
1051cb0ef41Sopenharmony_ci                         whitespace,
1061cb0ef41Sopenharmony_ci                         child);
1071cb0ef41Sopenharmony_ci    } else {
1081cb0ef41Sopenharmony_ci      per_process::Debug(DebugCategory::PERMISSION_MODEL,
1091cb0ef41Sopenharmony_ci                         "%s End of tree: %s\n",
1101cb0ef41Sopenharmony_ci                         whitespace,
1111cb0ef41Sopenharmony_ci                         node->prefix);
1121cb0ef41Sopenharmony_ci    }
1131cb0ef41Sopenharmony_ci  }
1141cb0ef41Sopenharmony_ci}
1151cb0ef41Sopenharmony_ci
1161cb0ef41Sopenharmony_ci// allow = '*'
1171cb0ef41Sopenharmony_ci// allow = '/tmp/,/home/example.js'
1181cb0ef41Sopenharmony_civoid FSPermission::Apply(const std::string& allow, PermissionScope scope) {
1191cb0ef41Sopenharmony_ci  using std::string_view_literals::operator""sv;
1201cb0ef41Sopenharmony_ci  for (const std::string_view res : SplitString(allow, ","sv)) {
1211cb0ef41Sopenharmony_ci    if (res == "*"sv) {
1221cb0ef41Sopenharmony_ci      if (scope == PermissionScope::kFileSystemRead) {
1231cb0ef41Sopenharmony_ci        deny_all_in_ = false;
1241cb0ef41Sopenharmony_ci        allow_all_in_ = true;
1251cb0ef41Sopenharmony_ci      } else {
1261cb0ef41Sopenharmony_ci        deny_all_out_ = false;
1271cb0ef41Sopenharmony_ci        allow_all_out_ = true;
1281cb0ef41Sopenharmony_ci      }
1291cb0ef41Sopenharmony_ci      return;
1301cb0ef41Sopenharmony_ci    }
1311cb0ef41Sopenharmony_ci    GrantAccess(scope, std::string(res.data(), res.size()));
1321cb0ef41Sopenharmony_ci  }
1331cb0ef41Sopenharmony_ci}
1341cb0ef41Sopenharmony_ci
1351cb0ef41Sopenharmony_civoid FSPermission::GrantAccess(PermissionScope perm, const std::string& res) {
1361cb0ef41Sopenharmony_ci  const std::string path = WildcardIfDir(res);
1371cb0ef41Sopenharmony_ci  if (perm == PermissionScope::kFileSystemRead) {
1381cb0ef41Sopenharmony_ci    granted_in_fs_.Insert(path);
1391cb0ef41Sopenharmony_ci    deny_all_in_ = false;
1401cb0ef41Sopenharmony_ci  } else if (perm == PermissionScope::kFileSystemWrite) {
1411cb0ef41Sopenharmony_ci    granted_out_fs_.Insert(path);
1421cb0ef41Sopenharmony_ci    deny_all_out_ = false;
1431cb0ef41Sopenharmony_ci  }
1441cb0ef41Sopenharmony_ci}
1451cb0ef41Sopenharmony_ci
1461cb0ef41Sopenharmony_cibool FSPermission::is_granted(PermissionScope perm,
1471cb0ef41Sopenharmony_ci                              const std::string_view& param = "") {
1481cb0ef41Sopenharmony_ci  switch (perm) {
1491cb0ef41Sopenharmony_ci    case PermissionScope::kFileSystem:
1501cb0ef41Sopenharmony_ci      return allow_all_in_ && allow_all_out_;
1511cb0ef41Sopenharmony_ci    case PermissionScope::kFileSystemRead:
1521cb0ef41Sopenharmony_ci      return !deny_all_in_ &&
1531cb0ef41Sopenharmony_ci             ((param.empty() && allow_all_in_) || allow_all_in_ ||
1541cb0ef41Sopenharmony_ci              is_tree_granted(&granted_in_fs_, param));
1551cb0ef41Sopenharmony_ci    case PermissionScope::kFileSystemWrite:
1561cb0ef41Sopenharmony_ci      return !deny_all_out_ &&
1571cb0ef41Sopenharmony_ci             ((param.empty() && allow_all_out_) || allow_all_out_ ||
1581cb0ef41Sopenharmony_ci              is_tree_granted(&granted_out_fs_, param));
1591cb0ef41Sopenharmony_ci    default:
1601cb0ef41Sopenharmony_ci      return false;
1611cb0ef41Sopenharmony_ci  }
1621cb0ef41Sopenharmony_ci}
1631cb0ef41Sopenharmony_ci
1641cb0ef41Sopenharmony_ciFSPermission::RadixTree::RadixTree() : root_node_(new Node("")) {}
1651cb0ef41Sopenharmony_ci
1661cb0ef41Sopenharmony_ciFSPermission::RadixTree::~RadixTree() {
1671cb0ef41Sopenharmony_ci  FreeRecursivelyNode(root_node_);
1681cb0ef41Sopenharmony_ci}
1691cb0ef41Sopenharmony_ci
1701cb0ef41Sopenharmony_cibool FSPermission::RadixTree::Lookup(const std::string_view& s,
1711cb0ef41Sopenharmony_ci                                     bool when_empty_return = false) {
1721cb0ef41Sopenharmony_ci  FSPermission::RadixTree::Node* current_node = root_node_;
1731cb0ef41Sopenharmony_ci  if (current_node->children.size() == 0) {
1741cb0ef41Sopenharmony_ci    return when_empty_return;
1751cb0ef41Sopenharmony_ci  }
1761cb0ef41Sopenharmony_ci
1771cb0ef41Sopenharmony_ci  unsigned int parent_node_prefix_len = current_node->prefix.length();
1781cb0ef41Sopenharmony_ci  const std::string path(s);
1791cb0ef41Sopenharmony_ci  auto path_len = path.length();
1801cb0ef41Sopenharmony_ci
1811cb0ef41Sopenharmony_ci  while (true) {
1821cb0ef41Sopenharmony_ci    if (parent_node_prefix_len == path_len && current_node->IsEndNode()) {
1831cb0ef41Sopenharmony_ci      return true;
1841cb0ef41Sopenharmony_ci    }
1851cb0ef41Sopenharmony_ci
1861cb0ef41Sopenharmony_ci    auto node = current_node->NextNode(path, parent_node_prefix_len);
1871cb0ef41Sopenharmony_ci    if (node == nullptr) {
1881cb0ef41Sopenharmony_ci      return false;
1891cb0ef41Sopenharmony_ci    }
1901cb0ef41Sopenharmony_ci
1911cb0ef41Sopenharmony_ci    current_node = node;
1921cb0ef41Sopenharmony_ci    parent_node_prefix_len += current_node->prefix.length();
1931cb0ef41Sopenharmony_ci    if (current_node->wildcard_child != nullptr &&
1941cb0ef41Sopenharmony_ci        path_len >= (parent_node_prefix_len - 2 /* slash* */)) {
1951cb0ef41Sopenharmony_ci      return true;
1961cb0ef41Sopenharmony_ci    }
1971cb0ef41Sopenharmony_ci  }
1981cb0ef41Sopenharmony_ci}
1991cb0ef41Sopenharmony_ci
2001cb0ef41Sopenharmony_civoid FSPermission::RadixTree::Insert(const std::string& path) {
2011cb0ef41Sopenharmony_ci  FSPermission::RadixTree::Node* current_node = root_node_;
2021cb0ef41Sopenharmony_ci
2031cb0ef41Sopenharmony_ci  unsigned int parent_node_prefix_len = current_node->prefix.length();
2041cb0ef41Sopenharmony_ci  int path_len = path.length();
2051cb0ef41Sopenharmony_ci
2061cb0ef41Sopenharmony_ci  for (int i = 1; i <= path_len; ++i) {
2071cb0ef41Sopenharmony_ci    bool is_wildcard_node = path[i - 1] == '*';
2081cb0ef41Sopenharmony_ci    bool is_last_char = i == path_len;
2091cb0ef41Sopenharmony_ci
2101cb0ef41Sopenharmony_ci    if (is_wildcard_node || is_last_char) {
2111cb0ef41Sopenharmony_ci      std::string node_path = path.substr(parent_node_prefix_len, i);
2121cb0ef41Sopenharmony_ci      current_node = current_node->CreateChild(node_path);
2131cb0ef41Sopenharmony_ci    }
2141cb0ef41Sopenharmony_ci
2151cb0ef41Sopenharmony_ci    if (is_wildcard_node) {
2161cb0ef41Sopenharmony_ci      current_node = current_node->CreateWildcardChild();
2171cb0ef41Sopenharmony_ci      parent_node_prefix_len = i;
2181cb0ef41Sopenharmony_ci    }
2191cb0ef41Sopenharmony_ci  }
2201cb0ef41Sopenharmony_ci
2211cb0ef41Sopenharmony_ci  if (UNLIKELY(per_process::enabled_debug_list.enabled(
2221cb0ef41Sopenharmony_ci          DebugCategory::PERMISSION_MODEL))) {
2231cb0ef41Sopenharmony_ci    per_process::Debug(DebugCategory::PERMISSION_MODEL, "Inserting %s\n", path);
2241cb0ef41Sopenharmony_ci    PrintTree(root_node_);
2251cb0ef41Sopenharmony_ci  }
2261cb0ef41Sopenharmony_ci}
2271cb0ef41Sopenharmony_ci
2281cb0ef41Sopenharmony_ci}  // namespace permission
2291cb0ef41Sopenharmony_ci}  // namespace node
230