C2Payload RESTProtocol::parseJsonResponse()

in libminifi/src/c2/protocols/RESTProtocol.cpp [39:129]


C2Payload RESTProtocol::parseJsonResponse(const C2Payload &payload, std::span<const std::byte> response) const {
  if (payload.getOperation() == Operation::acknowledge) {
    return {payload.getOperation(), state::UpdateState::READ_COMPLETE};
  }

  try {
    rapidjson::Document root;
    rapidjson::ParseResult ok = root.Parse(reinterpret_cast<const char*>(response.data()), response.size());
    if (ok) {
      std::string identifier;
      for (auto key : {"operationid", "operationId", "identifier"}) {
        if (root.HasMember(key)) {
          if (!root[key].IsNull()) {
            identifier = root[key].GetString();
          }
          break;
        }
      }

      rapidjson::SizeType size = 0;
      for (auto key : {"requested_operations", "requestedOperations"}) {
        if (root.HasMember(key)) {
          if (!root[key].IsNull()) {
            size = root[key].Size();
          }
          break;
        }
      }

      // neither must be there. We don't want assign array yet and cause an assertion error
      if (size == 0)
        return {payload.getOperation(), state::UpdateState::READ_COMPLETE};

      C2Payload new_payload(payload.getOperation(), state::UpdateState::NESTED);
      if (!identifier.empty())
        new_payload.setIdentifier(identifier);

      auto array = root.HasMember("requested_operations") ? root["requested_operations"].GetArray() : root["requestedOperations"].GetArray();

      for (const rapidjson::Value& request : array) {
        auto newOp = magic_enum::enum_cast<Operation>(request["operation"].GetString(), magic_enum::case_insensitive).value_or(Operation::heartbeat);
        C2Payload nested_payload(newOp, state::UpdateState::READ_COMPLETE);
        C2ContentResponse new_command(newOp);
        new_command.delay = 0;
        new_command.required = true;
        new_command.ttl = -1;

        // set the identifier if one exists
        for (auto key : {"operationid", "operationId", "identifier"}) {
          if (request.HasMember(key)) {
            if (request[key].IsNumber()) {
              new_command.ident = std::to_string(request[key].GetInt64());
            } else if (request[key].IsString()) {
              new_command.ident = request[key].GetString();
            } else {
              throw Exception(SITE2SITE_EXCEPTION, "Invalid type for " + std::string{key});
            }
            nested_payload.setIdentifier(new_command.ident);
            break;
          }
        }

        if (request.HasMember("name")) {
          new_command.name = request["name"].GetString();
        } else if (request.HasMember("operand")) {
          new_command.name = request["operand"].GetString();
        }

        for (auto key : {"content", "args"}) {
          if (request.HasMember(key) && request[key].IsObject()) {
            for (const auto &member : request[key].GetObject()) {
              new_command.operation_arguments[member.name.GetString()] = C2Value{member.value};
            }
            break;
          }
        }

        nested_payload.addContent(std::move(new_command));
        new_payload.addPayload(std::move(nested_payload));
      }

      // we have a response for this request
      return new_payload;
      // }
    } else {
      logger_->log_error("Failed to parse json response: {} at {}", rapidjson::GetParseError_En(ok.Code()), ok.Offset());
    }
  } catch (...) {
  }
  return {payload.getOperation(), state::UpdateState::READ_COMPLETE};
}