Multiple typo fixes
This shouldn't break anything, no variable name is changed. Documentation and strings are fixed, strings that do not come in a 'command' or those that are required for 'pwncat' to work are not changed.
This commit is contained in:
parent
2790a7a287
commit
e2929573be
4
IDEAS.md
4
IDEAS.md
|
@ -207,7 +207,7 @@ configuration is not set locally when `run` is executed, then the global
|
|||
configuration will be checked for matching arguments for the module.
|
||||
|
||||
```sh
|
||||
# Install a persistence mthod with a bind channel
|
||||
# Install a persistence method with a bind channel
|
||||
use persistence/system/cron
|
||||
set method channels/bind
|
||||
set schedule "* * */1 *"
|
||||
|
@ -303,7 +303,7 @@ pwncat.victim.progress.status("Here's a status update", task=task)
|
|||
|
||||
The progress bar itself will be managed by the `Victim` object. We can keep the standard
|
||||
now where iterative/generator based results are used to update a task, but also allows
|
||||
modules to directly call `pwncat.victim.progres.status`. This would do away with the `Status`
|
||||
modules to directly call `pwncat.victim.progress.status`. This would do away with the `Status`
|
||||
class. Further, it allows the `module.run` method to return the raw result of the underlying
|
||||
method allowing more flexibility in the return values of modules. It allows modules to have
|
||||
asynchronous (generator) return values.
|
||||
|
|
10
README.md
10
README.md
|
@ -22,7 +22,7 @@ for working with remote shells.
|
|||
- Disable history in the remote shell
|
||||
- Normalize shell prompt
|
||||
- Locate useful binaries (using `which`)
|
||||
- Attempt to spawn a pseudoterminal (pty) for a full interactive session
|
||||
- Attempt to spawn a pseudo-terminal (pty) for a full interactive session
|
||||
|
||||
`pwncat` knows how to spawn pty's with a few different methods and will
|
||||
cross-reference the methods with the executables previously enumerated. After
|
||||
|
@ -53,7 +53,7 @@ pwncat will take care of the rest.
|
|||
The libraries implementing the C2 are implemented at [pwncat-windows-c2].
|
||||
The DLLs for the C2 will be automatically downloaded from the targeted release
|
||||
for you. If you do not have internet connectivity on your target machine,
|
||||
you can tell pwncat to prestage the DLLs using the `--download-plugins`
|
||||
you can tell pwncat to pre-stage the DLLs using the `--download-plugins`
|
||||
argument. If you are running a release version of pwncat, you can also download
|
||||
a tarball of all built-in plugins from the releases page.
|
||||
|
||||
|
@ -77,7 +77,7 @@ the `stable` version.
|
|||
|
||||
The current `master` branch is `v0.4.3`. This version has
|
||||
overhauled a lot of the framework to support multiple platforms and
|
||||
multisession environments. Documentation for this version is available
|
||||
multi-session environments. Documentation for this version is available
|
||||
in the `latest` version on Read the Docs.
|
||||
|
||||
**v0.3.1 will not be updated further**
|
||||
|
@ -85,7 +85,7 @@ in the `latest` version on Read the Docs.
|
|||
## Modules
|
||||
|
||||
Recently, the architecture of the pwncat framework was redesigned to
|
||||
encorporate a generic "module" structure. All functionality is now
|
||||
incorporate a generic "module" structure. All functionality is now
|
||||
implemented as modules. This includes enumeration, persistence and
|
||||
privilege escalation. Interacting with modules is similar to most other
|
||||
post-exploitation platforms. You can utilize the familiar `run`, `search`
|
||||
|
@ -156,7 +156,7 @@ pwncat -i id_rsa user@10.10.10.10
|
|||
pwncat -p 2222 user@10.10.10.10
|
||||
pwncat user@10.10.10.10:2222
|
||||
# Reconnect utilizing installed persistence
|
||||
# If reconnection failes and no protocol is specified,
|
||||
# If reconnection fails and no protocol is specified,
|
||||
# SSH is used as a fallback.
|
||||
pwncat reconnect://user@10.10.10.10
|
||||
pwncat reconnect://user@c228fc49e515628a0c13bdc4759a12bf
|
||||
|
|
|
@ -3,7 +3,7 @@ Channels represent the basic communication object within pwncat. Each channel
|
|||
abstracts a communication method with a target. By default, pwncat implements
|
||||
a few standard channels: socket bind/connect and ssh.
|
||||
|
||||
A channel largely mimicks a standard socket, however exact compatibility with
|
||||
A channel largely mimics a standard socket, however exact compatibility with
|
||||
sockets was not the goal. Instead, it provides a low-level communication channel
|
||||
between the target and the attacker. Channels make no assumption about protocol
|
||||
of the C2 connection. This is the platform's job.
|
||||
|
|
|
@ -173,7 +173,7 @@ class Parameter:
|
|||
This class allows you to specify the syntax highlighting, tab completion
|
||||
and argparse settings for a command parameter in on go. The ``complete``
|
||||
argument tells pwncat how to tab complete your argument. The ``token``
|
||||
argument is normally ommitted but can be used to change the pygments
|
||||
argument is normally omitted but can be used to change the pygments
|
||||
syntax highlighting for your argument. All other arguments are passed
|
||||
directly to ``argparse`` when constructing the parser.
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ class Command(CommandDefinition):
|
|||
"""
|
||||
Connect to a remote victim. This command is only valid prior to an established
|
||||
connection. This command attempts to act similar to common tools such as netcat
|
||||
and ssh simultaneosly. Connection strings come in two forms. Firstly, pwncat
|
||||
and ssh simultaneously. Connection strings come in two forms. Firstly, pwncat
|
||||
can act like netcat. Using `connect [host] [port]` will connect to a bind shell,
|
||||
while `connect -l [port]` will listen for a reverse shell on the specified port.
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ class Command(CommandDefinition):
|
|||
)
|
||||
|
||||
for module in modules:
|
||||
# Rich will ellipsize the column, but we need to squeze
|
||||
# Rich will ellipsize the column, but we need to squeeze
|
||||
# white space and remove newlines. `textwrap.shorten` is
|
||||
# the easiest way to do that, so we use a large size for
|
||||
# width.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
This package defines all database objects. pwncat internally uses
|
||||
the ZODB database, which stores data as persistent Python objects.
|
||||
Each class defined under this package is a persistent Python
|
||||
class which is stored verabtim in the database. For documentation
|
||||
class which is stored verbatim in the database. For documentation
|
||||
on how to create persistent classes, please see the ZODB
|
||||
documentation.
|
||||
"""
|
||||
|
|
|
@ -25,7 +25,7 @@ def build_gtfo_ability(
|
|||
**kwargs,
|
||||
) -> Union["GTFOFileRead", "GTFOFileWrite", "GTFOExecute"]:
|
||||
r"""Build a escalation ability from a GTFOBins method. This will return
|
||||
one of of the GTFO ability classes based on the capabilties exposed by
|
||||
one of of the GTFO ability classes based on the capabilities exposed by
|
||||
the given GTFOBins method.
|
||||
|
||||
:param source: the generating module
|
||||
|
@ -64,7 +64,7 @@ class FileReadAbility(Fact):
|
|||
|
||||
:param source: generating module name
|
||||
:type source: str
|
||||
:param source_uid: the starting UID or None if it doesnt matter
|
||||
:param source_uid: the starting UID or None if it doesn't matter
|
||||
:type source_uid: Optional[Union[int, str]]
|
||||
:param uid: the target UID
|
||||
:type uid: Union[int, str]
|
||||
|
@ -98,7 +98,7 @@ class FileWriteAbility(Fact):
|
|||
|
||||
:param source: generating module name
|
||||
:type source: str
|
||||
:param source_uid: the starting UID or None if it doesnt matter
|
||||
:param source_uid: the starting UID or None if it doesn't matter
|
||||
:type source_uid: Optional[Union[int, str]]
|
||||
:param uid: the target UID
|
||||
:type uid: Union[int, str]
|
||||
|
@ -132,7 +132,7 @@ class ExecuteAbility(Fact):
|
|||
|
||||
:param source: generating module name
|
||||
:type source: str
|
||||
:param source_uid: the starting UID or None if it doesnt matter
|
||||
:param source_uid: the starting UID or None if it doesn't matter
|
||||
:type source_uid: Optional[Union[int, str]]
|
||||
:param uid: the target UID
|
||||
:type uid: Union[int, str]
|
||||
|
@ -162,7 +162,7 @@ class SpawnAbility(Fact):
|
|||
|
||||
:param source: generating module name
|
||||
:type source: str
|
||||
:param source_uid: the starting UID or None if it doesnt matter
|
||||
:param source_uid: the starting UID or None if it doesn't matter
|
||||
:type source_uid: Optional[Union[int, str]]
|
||||
:param uid: the target UID
|
||||
:type uid: Union[int, str]
|
||||
|
@ -191,7 +191,7 @@ class GTFOFileRead(FileReadAbility):
|
|||
|
||||
:param source: generating module name
|
||||
:type source: str
|
||||
:param source_uid: the starting UID or None if it doesnt matter
|
||||
:param source_uid: the starting UID or None if it doesn't matter
|
||||
:type source_uid: Optional[Union[int, str]]
|
||||
:param uid: the target UID
|
||||
:type uid: Union[int, str]
|
||||
|
@ -223,7 +223,7 @@ class GTFOFileRead(FileReadAbility):
|
|||
errors: str = None,
|
||||
newline: str = None,
|
||||
):
|
||||
"""Read the file data using a GTFO bins reader"""
|
||||
"""Read the file data using a GTFObins reader"""
|
||||
|
||||
if any(c not in "rb" for c in mode):
|
||||
raise ValueError("only r/b modes allowed")
|
||||
|
@ -288,7 +288,7 @@ class GTFOFileWrite(FileWriteAbility):
|
|||
|
||||
:param source: generating module name
|
||||
:type source: str
|
||||
:param source_uid: the starting UID or None if it doesnt matter
|
||||
:param source_uid: the starting UID or None if it doesn't matter
|
||||
:type source_uid: Optional[Union[int, str]]
|
||||
:param uid: the target UID
|
||||
:type uid: Union[int, str]
|
||||
|
@ -320,7 +320,7 @@ class GTFOFileWrite(FileWriteAbility):
|
|||
errors: str = None,
|
||||
newline: str = None,
|
||||
):
|
||||
"""Read the file data using a GTFO bins reader"""
|
||||
"""Read the file data using a GTFObins reader"""
|
||||
|
||||
if any(c not in "wb" for c in mode):
|
||||
raise ValueError("only w/b modes allowed")
|
||||
|
@ -385,7 +385,7 @@ class GTFOExecute(ExecuteAbility):
|
|||
|
||||
:param source: generating module name
|
||||
:type source: str
|
||||
:param source_uid: the starting UID or None if it doesnt matter
|
||||
:param source_uid: the starting UID or None if it doesn't matter
|
||||
:type source_uid: Optional[Union[int, str]]
|
||||
:param uid: the target UID
|
||||
:type uid: Union[int, str]
|
||||
|
@ -408,7 +408,7 @@ class GTFOExecute(ExecuteAbility):
|
|||
self.kwargs = kwargs
|
||||
|
||||
def send_command(self, session, command: bytes = None):
|
||||
"""Send the command to the target for this GTFObin"""
|
||||
"""Send the command to the target for this GTFObins"""
|
||||
|
||||
def Popen(self, session, *args, **kwargs):
|
||||
"""Emulate the platform.Popen method for execution as another user"""
|
||||
|
|
|
@ -49,7 +49,7 @@ class Tamper(Fact):
|
|||
:param session: the session on which to operate
|
||||
:type session: pwncat.manager.Session
|
||||
"""
|
||||
raise ModuleFailed("not reverable")
|
||||
raise ModuleFailed("not revertable")
|
||||
|
||||
def _annotate_title(self, session, title):
|
||||
"""Just a helper for annotating the description with details on
|
||||
|
|
|
@ -163,7 +163,7 @@ class Method:
|
|||
# Make sure both sudo_spec and sudo_user are provided if sudo_spec is
|
||||
assert spec is None or (spec is not None and user is not None)
|
||||
|
||||
# Make sure we can use this spec, and get remainig arguments
|
||||
# Make sure we can use this spec, and get remaining arguments
|
||||
if spec is not None:
|
||||
command, args = self.sudo_args(binary_path, spec)
|
||||
args = gtfo.resolve_binaries(
|
||||
|
@ -405,7 +405,7 @@ class GTFOBins:
|
|||
|
||||
if spec != "ALL":
|
||||
# This is the harder case. We have a specific specification for the
|
||||
# command wecan run.
|
||||
# command we can run.
|
||||
|
||||
# If there are arguments, remove them and grab the first item, which
|
||||
# will be the path or binary name
|
||||
|
@ -489,7 +489,7 @@ class GTFOBins:
|
|||
quote = False
|
||||
# Find the remote binary that matches
|
||||
value = self.which(key, quote=quote)
|
||||
# Whoops! No dependancy
|
||||
# Whoops! No dependency
|
||||
if value is None:
|
||||
raise MissingBinary(key)
|
||||
# Next time, we have it
|
||||
|
|
|
@ -315,7 +315,7 @@ class BaseModule(metaclass=BaseModuleMeta):
|
|||
but only return one scalar value, setting this to true will collapse
|
||||
an array with only a single object to it's scalar value. """
|
||||
PLATFORM: typing.List[typing.Type["pwncat.platform.Platform"]] = []
|
||||
""" The platform this module is compatibile with (can be multiple) """
|
||||
""" The platform this module is compatible with (can be multiple) """
|
||||
|
||||
def __init__(self):
|
||||
self.progress = None
|
||||
|
|
|
@ -70,7 +70,7 @@ class Module(pwncat.modules.BaseModule):
|
|||
PLATFORM = None
|
||||
|
||||
def run(self, session, output, modules, types, clear, cache, exclude):
|
||||
"""Perform a enumeration of the given moduels and save the output"""
|
||||
"""Perform a enumeration of the given modules and save the output"""
|
||||
|
||||
module_names = modules
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ class AppendPasswd(EscalationReplace):
|
|||
|
||||
|
||||
class Module(EnumerateModule):
|
||||
"""Check for possible methods of escalation via modiying /etc/passwd"""
|
||||
"""Check for possible methods of escalation via modifying /etc/passwd"""
|
||||
|
||||
PROVIDES = ["escalate.replace"]
|
||||
SCHEDULE = Schedule.PER_USER
|
||||
|
|
|
@ -44,7 +44,7 @@ class Module(EnumerateModule):
|
|||
def enumerate(self, session: "pwncat.manager.Session"):
|
||||
|
||||
# This forces the session to enumerate users FIRST, so we don't run
|
||||
# into trying to enumerate _whilest_ enumerating SUID binaries...
|
||||
# into trying to enumerate _whilst_ enumerating SUID binaries...
|
||||
# since we can't yet run multiple processes at the same time
|
||||
session.find_user(uid=0)
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ class Module(EnumerateModule):
|
|||
for method in session.platform.gtfo.iter_sudo(
|
||||
command, caps=Capability.ALL
|
||||
):
|
||||
# Build a generic GTFO bins capability
|
||||
# Build a generic GTFObins capability
|
||||
yield build_gtfo_ability(
|
||||
source=self.name,
|
||||
uid=0,
|
||||
|
|
|
@ -261,7 +261,7 @@ class Module(EnumerateModule):
|
|||
for line in result.split("\n"):
|
||||
line = line.rstrip()
|
||||
|
||||
# Skipe header lines
|
||||
# Skip header lines
|
||||
if not line.startswith(" ") and not line.startswith("\t"):
|
||||
continue
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ class Module(EnumerateModule):
|
|||
and operating system name (normally GNU/Linux).
|
||||
|
||||
This module also provides a similar enumeration to the
|
||||
common Linux Exploit Suggestor, and will report known
|
||||
common Linux Exploit Suggester, and will report known
|
||||
vulnerabilities which are applicable to the detected
|
||||
kernel version.
|
||||
"""
|
||||
|
|
|
@ -89,7 +89,7 @@ class Module(ImplantModule):
|
|||
with session.platform.open("/etc/passwd", "r") as filp:
|
||||
passwd_contents = list(filp)
|
||||
except (FileNotFoundError, PermissionError):
|
||||
raise ModuleFailed("faild to read /etc/passwd")
|
||||
raise ModuleFailed("failed to read /etc/passwd")
|
||||
|
||||
# Hash the password
|
||||
yield Status("hashing password")
|
||||
|
|
|
@ -148,7 +148,7 @@ class Module(BaseModule):
|
|||
raise ModuleFailed(f"while importing Invoke-BloodHound: {exc}")
|
||||
|
||||
# Try to create a temporary file. We're just going to delete it, but
|
||||
# this gives us a tangeable temporary path to put the zip file.
|
||||
# this gives us a tangible temporary path to put the zip file.
|
||||
yield Status("locating a suitable temporary file location")
|
||||
with session.platform.tempfile(suffix="zip", mode="w") as filp:
|
||||
file_path = filp.name
|
||||
|
|
|
@ -21,7 +21,7 @@ class DomainObject(Fact):
|
|||
return self.domain[name]
|
||||
|
||||
def title(self, session: "pwncat.manager.Session"):
|
||||
return f"Active Dirctory Domain: [magenta]{self.domain['Name']}[/magenta]"
|
||||
return f"Active Directory Domain: [magenta]{self.domain['Name']}[/magenta]"
|
||||
|
||||
def description(self, session: "pwncat.manager.Session"):
|
||||
output = []
|
||||
|
|
|
@ -394,7 +394,7 @@ class Path:
|
|||
raise OSError(exc.stdout) from exc
|
||||
|
||||
def touch(self, mode: int = 0o666, exist_ok: bool = True):
|
||||
"""Createa file at this path. If the file already exists, function
|
||||
"""Create a file at this path. If the file already exists, function
|
||||
succeeds if exist_ok is true (and it's modification time is updated).
|
||||
Otherwise FileExistsError is raised."""
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"""
|
||||
The Linux platform provides Linux shell support ontop of any channel.
|
||||
The Linux platfrom expects the channel to expose a shell whose stdio
|
||||
The Linux platform expects the channel to expose a shell whose stdio
|
||||
is connected directly to the channel IO. At a minimum stdin and stdout
|
||||
must be connected.
|
||||
|
||||
|
@ -122,14 +122,14 @@ class PopenLinux(pwncat.subprocess.Popen):
|
|||
except ValueError:
|
||||
pass
|
||||
|
||||
# This gets a 'lil... funky... Normally, the ChannelFile
|
||||
# This gets a 'lil... funky... Normally, the `ChannelFile`
|
||||
# wraps a non-blocking socket in a blocking file object
|
||||
# because this what we normally we want and allows us
|
||||
# because this is what we normally want and it allows us
|
||||
# to implement our own timeouts. However, here we want
|
||||
# a non-blocking call to check for EOF, so we set the
|
||||
# internal ``blocking`` flag to False which can cause
|
||||
# a `BlockingIOError` caught below. We need to do this
|
||||
# in a nested `try-finaly` so we gaurantee catching it
|
||||
# in a nested `try-finally` so we guarantee catching it
|
||||
# and resetting the flag before calling `_receive_returncode`.
|
||||
try:
|
||||
try:
|
||||
|
@ -697,7 +697,7 @@ class Linux(Platform):
|
|||
continue
|
||||
break
|
||||
else:
|
||||
raise PlatformError("no avialable pty methods")
|
||||
raise PlatformError("no available pty methods")
|
||||
|
||||
# When starting a pty, history is sometimes re-enabled
|
||||
self.disable_history()
|
||||
|
|
|
@ -5,13 +5,13 @@ utilize the C2 libraries located at `pwncat-windows-c2 <https://github.com/caleb
|
|||
This will be automatically downloaded to the directory identified by the
|
||||
``windows_c2_dir`` configuration which defaults to ``~/.local/share/pwncat/``.
|
||||
It will be uploaded and executed via ``Install-Util`` in order to automatically
|
||||
bypass AppLocker, and will provide you an unlogged, unconstrained powershell
|
||||
bypass AppLocker, and will provide you an un-logged, unconstrained powershell
|
||||
session as well as basic process and file IO routines.
|
||||
|
||||
When operating in a platform-specific environment, you can safely execute multiple
|
||||
processes and open multiple files with this platform. However, you should be
|
||||
careful to cleanup all processes and files prior to return from your method
|
||||
or code as the C2 will not attempt to garbage collect file or proces handles.
|
||||
or code as the C2 will not attempt to garbage collect file or process handles.
|
||||
"""
|
||||
import sys
|
||||
import gzip
|
||||
|
@ -64,7 +64,7 @@ class ProtocolError(PlatformError):
|
|||
|
||||
@dataclass
|
||||
class stat_result:
|
||||
"""Python `os` doesn't provide a way to sainly construct a stat_result
|
||||
"""Python `os` doesn't provide a way to sanely construct a stat_result
|
||||
so I created this."""
|
||||
|
||||
st_mode = 0
|
||||
|
@ -635,7 +635,7 @@ function prompt {
|
|||
"\\Windows\\System32\\spool\\SERVERS",
|
||||
"\\Windows\\System32\\spool\\drivers\\color",
|
||||
"\\Windows\\System32\\Tasks\\Microsoft\\Windows\\SyncCenter",
|
||||
"\\Windows\\System32\\Tasks_Migrated (after peforming a version upgrade of Windows 10)",
|
||||
"\\Windows\\System32\\Tasks_Migrated (after performing a version upgrade of Windows 10)",
|
||||
"\\Windows\\SysWOW64\\FxsTmp",
|
||||
"\\Windows\\SysWOW64\\com\\dmp",
|
||||
"\\Windows\\SysWOW64\\Tasks\\Microsoft\\Windows\\SyncCenter",
|
||||
|
@ -941,7 +941,7 @@ function prompt {
|
|||
errors: str = None,
|
||||
newline: str = None,
|
||||
):
|
||||
"""Mimick the built-in open method."""
|
||||
"""Mimic the built-in open method."""
|
||||
|
||||
# Ensure all mode properties are valid
|
||||
for char in mode:
|
||||
|
@ -1038,7 +1038,7 @@ function prompt {
|
|||
|
||||
def new_item(self, **kwargs):
|
||||
"""Run the `New-Item` commandlet with specified arguments and
|
||||
raise the appropriate local exception if requried. For a list of
|
||||
raise the appropriate local exception if required. For a list of
|
||||
valid arguments, see the New-Item help documentation."""
|
||||
|
||||
command = "New-Item "
|
||||
|
@ -1496,7 +1496,7 @@ function prompt {
|
|||
# Encode the assembly
|
||||
assembly = base64.b64encode(content).decode("utf-8")
|
||||
|
||||
# Load the assembly. Let protocol errors propogate
|
||||
# Load the assembly. Let protocol errors propagate
|
||||
ident = self.run_method("Reflection", "load", assembly)
|
||||
|
||||
plugin = DotNetPlugin(self, name, checksum, ident)
|
||||
|
|
|
@ -95,7 +95,7 @@ class Popen:
|
|||
|
||||
def communicate(self, input: bytes = None, timeout: float = None):
|
||||
"""Interact with process: Send data to stdin. Read data from stdout
|
||||
and stderr, until end-of-file is readched. Wait for the process to
|
||||
and stderr, until end-of-file is reached. Wait for the process to
|
||||
terminate and set the ``returncode`` attribute. The optional ``input``
|
||||
argument should be data to be sent to the child process, or None, if
|
||||
no data should be sent to the child. If streams were opened in text mode,
|
||||
|
|
|
@ -87,7 +87,7 @@ class Target(persistent.Persistent):
|
|||
self.tampers: persistent.list.PersistentList = persistent.list.PersistentList()
|
||||
""" List of files/properties of the target that have been modified and/or created. """
|
||||
self.users: persistent.list.PersistentList = persistent.list.PersistentList()
|
||||
""" List of users known on the target system (may not be all-encompasing depending on access) """
|
||||
""" List of users known on the target system (may not be all-encompassing depending on access) """
|
||||
self.utilities: OOBTree() = OOBTree()
|
||||
""" Mapping of utility names to paths. This is mainly used on Unix platforms to identify binaries available in the path. """
|
||||
self.implants: persistent.list.PersistentList = persistent.list.PersistentList()
|
||||
|
|
|
@ -156,7 +156,7 @@ def join(argv: List[str]):
|
|||
def quote(token: str):
|
||||
"""Quote the token much like shlex.quote, except don't use single quotes
|
||||
this will escape any double quotes in the string and wrap it in double
|
||||
quotes. If there are no spaces, it returns the stirng unchanged."""
|
||||
quotes. If there are no spaces, it returns the string unchanged."""
|
||||
for c in token:
|
||||
if c in string.whitespace:
|
||||
break
|
||||
|
|
|
@ -75,7 +75,7 @@ set -g db "memory://"
|
|||
yield session
|
||||
break
|
||||
except ChannelError:
|
||||
# This seems to be because of the contaiener setup, so we just add
|
||||
# This seems to be because of the container setup, so we just add
|
||||
# a little sleep in
|
||||
time.sleep(2)
|
||||
else:
|
||||
|
|
Loading…
Reference in New Issue