Ctrl K
ComunidadGuías…

tests/test_api.py

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/usr/bin/env python
# encoding: UTF-8

"""
This file is part of Commix Project (https://commixproject.com).
Copyright (c) 2014-2026 Anastasios Stasinopoulos (@ancst).

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

For more see the file 'readme/COPYING' for copying permission.
"""

import os
import shutil
import socket
import tempfile
import unittest

from tests._testutils import stage
from src.utils import api
from src.core.parse import cmdline as menu

"""
Fail closed: the option logic is settled before anything is served, so a test of it that reaches for
the network is a broken test rather than a slow one.
"""
def _no_transport(*args, **kwargs):
  raise RuntimeError("The API's option handling is exercised offline; no transport may be opened.")

class _Offline(unittest.TestCase):
  def setUp(self):
    self.saved = (socket.socket, socket.create_connection)
    socket.socket, socket.create_connection = _no_transport, _no_transport
    self.directory = tempfile.mkdtemp(prefix="commix-api-")
    self.task = api.Task("0123456789abcdef", self.directory)

  def tearDown(self):
    socket.socket, socket.create_connection = self.saved
    shutil.rmtree(self.directory, ignore_errors=True)

class OptionNames(unittest.TestCase):
  """
  Every spelling the command line takes answers to the same option, since the API is asked in
  whichever of them the caller happens to know.
  """
  def test_an_option_answers_to_its_store_and_to_its_flags(self):
    for spelling in ("url", "--url", "-u"):
      self.assertIsNotNone(api._option(spelling.lstrip("-")), spelling + " names no option")
    self.assertEqual(api._option("url").dest, api._option("u").dest)

  def test_a_name_the_parser_does_not_know_names_nothing(self):
    self.assertIsNone(api._option("not_an_option_of_any_kind"))

class RefusedOptions(_Offline):
  """
  The options a task may not be given. Three of these lists exist for different reasons, and the
  first is the one that matters most: every option on it would act on the host serving the API
  rather than on the target, so a task that could set one would be remote code execution wearing
  the API's own clothes.
  """
  def test_an_option_that_would_act_on_the_api_host_is_refused(self):
    for name in sorted(api.REFUSED_OPTIONS):
      refusal = self.task.set_option(name, "whatever")
      self.assertIsNotNone(refusal, "'" + name + "' was accepted over the API")
      self.assertIn("cannot be set over the API", refusal)
      self.assertNotIn(name, [k for k, v in self.task.options.items() if v == "whatever"])

  def test_an_option_the_server_settles_itself_is_refused(self):
    for name in api.SERVER_OWNED_OPTIONS:
      refusal = self.task.set_option(name, "whatever")
      self.assertIsNotNone(refusal, "'" + name + "' was accepted over the API")
      self.assertIn("settled by the server", refusal)

  def test_an_option_a_run_is_never_given_is_refused(self):
    # Named rather than swept: the point is that the list is not empty and is actually consulted.
    self.assertTrue(api.UNCARRIED_OPTIONS, "no option is listed as uncarried")
    for name in api.UNCARRIED_OPTIONS:
      if api._option(name) is None:
        continue
      self.assertIsNotNone(self.task.set_option(name, "whatever"), "'" + name + "' was accepted")

  def test_an_unknown_option_is_refused_by_name(self):
    refusal = self.task.set_option("--not-an-option", "x")
    self.assertIsNotNone(refusal)
    self.assertIn("not-an-option", refusal)

class AcceptedOptions(_Offline):
  """
  What the API does take, and what it does with it.
  """
  def test_an_ordinary_option_is_kept_where_the_run_reads_it(self):
    self.assertIsNone(self.task.set_option("url", "http://target.invalid/x?a=1"))
    self.assertEqual("http://target.invalid/x?a=1", self.task.options["url"])

  def test_an_option_is_taken_by_its_flag_as_well_as_its_name(self):
    self.assertIsNone(self.task.set_option("--level", "3"))
    self.assertEqual(3, self.task.options["level"])

  def test_a_value_the_parser_would_not_take_is_refused_here(self):
    refusal = self.task.set_option("level", "three")
    self.assertIsNotNone(refusal, "a non-numeric level was accepted")
    self.assertIn("The option", refusal)

class QueryReading(unittest.TestCase):
  """
  What the handler reads off a request, settled without one being made.
  """
  def test_a_value_is_read_as_what_it_is_written_as(self):
    self.assertIs(True, api._typed("true"))
    self.assertIs(False, api._typed("FALSE"))
    self.assertEqual(3, api._typed("3"))
    self.assertEqual("3.1", api._typed("3.1"))
    self.assertEqual("ancst", api._typed("ancst"))

  def test_bounds_are_read_where_given_and_left_alone_where_not(self):
    self.assertEqual((2, 9), api._bounds({"start": ["2"], "end": ["9"]}))
    self.assertEqual((None, None), api._bounds({}))
    self.assertEqual((None, None), api._bounds({"start": ["x"], "end": []}))

if __name__ == "__main__":
  unittest.main()