trueseeing


Nametrueseeing JSON
Version 2.2.2 PyPI version JSON
download
home_pageNone
SummaryTrueseeing is a non-decompiling Android application vulnerability scanner.
upload_time2024-03-19 19:52:44
maintainerNone
docs_urlNone
authorNone
requires_python>=3.9
licenseNone
keywords android security pentest hacking
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # README

![Last release](https://img.shields.io/github/v/release/alterakey/trueseeing)
![Last release date](https://img.shields.io/github/release-date-pre/alterakey/trueseeing)
![Main branch deploy status](https://github.com/alterakey/trueseeing/workflows/deploy/badge.svg)
![Main branch last commit](https://img.shields.io/github/last-commit/alterakey/trueseeing/main)

trueseeing is a fast, accurate and resillient vulnerability scanner for Android apps.  We operate on the Dalvik VM level -- i.e. we don't care if the target app is obfuscated or not.

## Capability

Currently we can:

* Automatically scan app for vulnerabilities, reporting in HTML/JSON/text format (see below)
* Manipulate app for easier analysis: e.g. enabling debug bit, enabling full backup, disabling TLS pinning, manipulating target API level, injecting frida-gadget, etc.
* Examine app for general information
* Copy in/out app data through debug interface
* Search for certain calls/consts/sput/iput
* Deduce constants/typesets for args of op
* etc.

## Installation

### Containers

NOTE:
 * As of 2.1.9, we are on ghcr.io. (Docker Hub is somewhat deprecated)
 * Requires adbd in the host to control devices.

We provide containers so you can use right away as follows; now this is also the recommended way, and the only way if you are on Windows, to run:

    $ docker run --rm -v $(pwd):/out -v ts2:/cache ghcr.io/alterakey/trueseeing

If you want to run statelessly you omit mounting volume onto /cache (not recommended for day-to-day use though; also see [#254](https://github.com/alterakey/trueseeing/issues/254)):

    $ docker run --rm -v $(pwd):/out ghcr.io/alterakey/trueseeing


### With pip

Alternatively, you can install our package with pip as follows. This form of installation might be useful for extensions, as it grants them the greatest freedom. Just remember you need a JRE and Android SDK (optionally; to mess with devices):

    $ pip install --user trueseeing
    $ trueseeing

## Usage

### Interactive mode

You can interactively scan/analyze/patch/etc. apps -- making it the ideal choice for manual analysis:

    $ trueseeing target.apk
    [+] trueseeing 2.2.2
    ts[target.apk]> ?
    ...
    ts[target.apk]> i                      # show generic information
    ...
    ts[target.apk]> pf AndroidManifest.xml # show manifest file
    ...
    ts[target.apk]> a                      # analyze resources too
    ...
    ts[target.apk]> /s something           # search text
    ...
    ts[target.apk]> as                     # scan
    ...
    [+] done, found 6403 issues (174.94 sec.)
    ts[target.apk]> gh report.html

### Batch mode

We accept an inline command (`-c`) or script file (`-i`) to run before giving you prompt, as well as quitting right away instead of prompting (`-q`; we don't require a tty in this mode!).

You can use the features to conduct a batch scan, as follows e.g. to dump findings right onto the stderr:

    $ trueseeing -eqc 'as' target.apk

To generate a report file in HTML format:

    $ trueseeing -eqc 'as;gh report.html' target.apk

To generate a report file in JSON format:

    $ trueseeing -eqc 'as;gj report.json' target.apk

To get report generated in stdout, omit filename from final `g*` command:

    $ trueseeing -eqc 'as;gh' target.apk > report.html
    $ trueseeing -eqc 'as;gj' target.apk > report.json

### Non-interactive scan mode (deprecated)

Traditionally, you can scan apps with the following command line to get findings listed in stderr:

    $ trueseeing --scan target.apk

To generate a report in HTML format:

    $ trueseeing --scan --scan-output report.html target.apk
    $ trueseeing --scan --scan-report=html --scan-output report.html target.apk

To generate a report in JSON format:

    $ trueseeing --scan --scan-report=json --scan-output report.json target.apk

To get report generated in stdout, specify '-' as filename:

    $ trueseeing --scan --scan-output - target.apk > report.html
    $ trueseeing --scan --scan-report=html --scan-output - target.apk > report.html
    $ trueseeing --scan --scan-report=json --scan-output - target.apk > report.json

## Advanced Usages

### Extensions

You can write your own commands and signatures as extensions.  Extensions are placed under `/ext` (containers) or `~/.trueseeing2/extensions/` (pip) . Alternatively you can distribute your extensions as wheels. We provide type information so you can not only type-check your extensions with [mypy](https://github.com/python/mypy) but also get a decent assist from IDEs. See the details section for details.

## Build

You can build it as follows:

    $ docker build -t trueseeing https://github.com/alterakey/trueseeing.git#main

To build wheels you can do with [flit](https://flit.pypa.io/en/stable/), as follows:

    $ flit build

To hack it, you need to create a proper build environment. To create one, set up a venv, install [flit](https://flit.pypa.io/en/stable/) in there, and have it pull dependencies and validating toolchains; esp. [mypy](https://github.com/python/mypy) and [pflake8](https://github.com/csachs/pyproject-flake8).  In short, do something like this:

    $ git clone https://github.com/alterakey/trueseeing.git wc
    $ python3 -m venv wc/.venv
    $ source wc/.venv/bin/activate
    (.venv) $ pip install flit
    (.venv) $ flit install --deps=develop -s
    (.venv) $ (... hack ...)
    (.venv) $ trueseeing ...                         # to run
    (.venv) $ mypy trueseeing && pflake8 trueseeing  # to validate
    Success: no issues found in XX source files
    (.venv) $ flit build                             # to build (wheel)
    (.venv) $ docker build -t trueseeing .           # to build (container)


## Details

### Vulnerability Classes

Currently we can detect the following class of vulnerabilities, largely ones covered in OWASP Mobile Top 10 - 2016:

  * Improper Platform Usage (M1)

    * Debuggable
    * Inadvent publishing of Activities, Services, ContentProviders, BroadcastReceivers

  * Insecure Data (M2)

    * Backupable (i.e. suspectible to the backup attack)
    * Insecure file permissions
    * Logging

  * Insecure Commnications (M3)

    * Lack of pinning (i.e. suspictible to the TLS interception attack)
    * Use of cleartext HTTP
    * Tamperable WebViews

  * Insufficient Cryptography (M5)

    * Hardcoded passphrase/secret keys
    * Vernum ciphers with static keys
    * Use of the ECB mode

  * Client Code Quality Issues (M7)

    * Reflectable WebViews (i.e. XSSs in such views should be escalatable to remote code executions via JS reflection)
    * Usage of insecure policy on mixed contents

  * Code Tampering (M8)

    * Hardcoded certificates

  * Reverse Engineering (M9)

    * Lack of obfuscation

### Extension API

Our extension API lays under the `trueseeing.api` package. As we provide type information with it, your IDE will assist you when writing your extensions.

#### Commands

To define new commands, implement `trueseeing.api.Command` and advertise them.

The following class will provide a sample command as `t`, for example:

```python
from typing import TYPE_CHECKING
from trueseeing.api import Command
from trueseeing.core.ui import ui
if TYPE_CHECKING:
  from trueseeing.api import CommandMap, CommandPatternMap, ModifierMap, OptionMap, ConfigMap

class MyCommand(Command):
  @staticmethod
  def create() -> Command:
    return MyCommand()

  def get_commands(self) -> CommandMap:
    return {'t':dict(e=self._test, n='t', d='sample command')}

  def get_command_patterns(self) -> CommandPatternMap:
    return dict()

  def get_modifiers(self) -> ModifierMap:
     return dict()

  def get_options(self) -> OptionMap:
    return dict()

  def get_configs(self) -> ConfigMap:
    return dict()

  async def _test(self) -> None:
    ui.info('hello world')
```

#### Signatures

To define new signatures, implement `trueseeing.api.Signature` and advertise them.

The following class will provide a sample detector as `my-sig`, for example:

```python
from typing import TYPE_CHECKING
from trueseeing.api import Signature
if TYPE_CHECKING:
  from trueseeing.api import SignatureMap, ConfigMap

class MySignature(Signature):
  @staticmethod
  def create() -> Signature:
    return MySignature()

  def get_sigs(self) -> SignatureMap:
    return {'my-sig':dict(e=self._detect, d='sample signature')}

  def get_configs(self) -> ConfigMap:
    return dict()

  async def _detect(self) -> None:
    self._helper.raise_issue(
      self._helper.build_issue(
        sigid='my-sig',
        title='hello world',
        cvss='CVSS:3.0/AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N/',
      )
    )
```


#### File Formats

To define new file formats, firstly implement `Context`s (ABC) for your formats, then implement `trueseeing.api.FileFormatHandler` to create and return their instances, and advertise them.

The following class will provide APK file support under the type named `apk2`, for example:

```python
from typing import TYPE_CHECKING
from trueseeing.api import FileFormatHandler
from trueseeing.core.android.context import APKContext

if TYPE_CHECKING:
  from typing import Optional, Set
  from trueseeing.api import FormatMap, ConfigMap
  from trueseeing.core.context import Context, ContextType

class MyAPKContext(APKContext):
  # Use a different context type
  def _get_type(self) -> Set[ContextType]:
    return {'apk2'}

class APKFileFormatHandler(FileFormatHandler):
  @staticmethod
  def create() -> FileFormatHandler:
    return APKFileFormatHandler()

  def get_formats(self) -> FormatMap:
    return {'apk2':dict(e=self._handle, r=r'\.apk$', d='sample file format')}

  def get_configs(self) -> ConfigMap:
    return dict()

  def _handle(self, path: str) -> Optional[Context]:
    return MyAPKContext(path)
```

Then make sure you check for the type of the context in your signatures, making them ignored on unsupported contexts:

```python
context = self._helper.get_context().require_type('apk2')
```

Upon successful check, `require_type(...)` will try to downcast them to appropriate types for your convenience.

But by design it works only for known types (currently, the `apk`).  So if you are defining some detailed interface in your new context classes as we do for the `apk` type, you need to do a downcast here i.e.:

```python
context: MyAPKContext = self._helper.get_context().require_type('apk2')  # type:ignore[assignment]
```

It is possible to define multiple formats matching the same pattern. We evaluate patterns in the order of from the most stringent (i.e. long) to the least. You use the `-F` switch to force some format to use with the target file, e.g.:

```
$ trueseeing -F apk2 target.apk
```

#### Package requirements

Extensions can be either: a) any package placed under `/ext` (container) or `~/.trueseeing2/extensions` (pip), or b) any installed module named with the prefix of `trueseeing_ext0_`.

### Origin of Project Name?

The D&D spell, [True Seeing](https://www.dandwiki.com/wiki/SRD:True_Seeing).

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "trueseeing",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "android,security,pentest,hacking",
    "author": null,
    "author_email": "Takahiro Yoshimura <alterakey@protonmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/06/e4/6d212a0e77cae21fc9aa04764466826c255057d57600c5a2ffe75f8538ba/trueseeing-2.2.2.tar.gz",
    "platform": null,
    "description": "# README\n\n![Last release](https://img.shields.io/github/v/release/alterakey/trueseeing)\n![Last release date](https://img.shields.io/github/release-date-pre/alterakey/trueseeing)\n![Main branch deploy status](https://github.com/alterakey/trueseeing/workflows/deploy/badge.svg)\n![Main branch last commit](https://img.shields.io/github/last-commit/alterakey/trueseeing/main)\n\ntrueseeing is a fast, accurate and resillient vulnerability scanner for Android apps.  We operate on the Dalvik VM level -- i.e. we don't care if the target app is obfuscated or not.\n\n## Capability\n\nCurrently we can:\n\n* Automatically scan app for vulnerabilities, reporting in HTML/JSON/text format (see below)\n* Manipulate app for easier analysis: e.g. enabling debug bit, enabling full backup, disabling TLS pinning, manipulating target API level, injecting frida-gadget, etc.\n* Examine app for general information\n* Copy in/out app data through debug interface\n* Search for certain calls/consts/sput/iput\n* Deduce constants/typesets for args of op\n* etc.\n\n## Installation\n\n### Containers\n\nNOTE:\n * As of 2.1.9, we are on ghcr.io. (Docker Hub is somewhat deprecated)\n * Requires adbd in the host to control devices.\n\nWe provide containers so you can use right away as follows; now this is also the recommended way, and the only way if you are on Windows, to run:\n\n    $ docker run --rm -v $(pwd):/out -v ts2:/cache ghcr.io/alterakey/trueseeing\n\nIf you want to run statelessly you omit mounting volume onto /cache (not recommended for day-to-day use though; also see [#254](https://github.com/alterakey/trueseeing/issues/254)):\n\n    $ docker run --rm -v $(pwd):/out ghcr.io/alterakey/trueseeing\n\n\n### With pip\n\nAlternatively, you can install our package with pip as follows. This form of installation might be useful for extensions, as it grants them the greatest freedom. Just remember you need a JRE and Android SDK (optionally; to mess with devices):\n\n    $ pip install --user trueseeing\n    $ trueseeing\n\n## Usage\n\n### Interactive mode\n\nYou can interactively scan/analyze/patch/etc. apps -- making it the ideal choice for manual analysis:\n\n    $ trueseeing target.apk\n    [+] trueseeing 2.2.2\n    ts[target.apk]> ?\n    ...\n    ts[target.apk]> i                      # show generic information\n    ...\n    ts[target.apk]> pf AndroidManifest.xml # show manifest file\n    ...\n    ts[target.apk]> a                      # analyze resources too\n    ...\n    ts[target.apk]> /s something           # search text\n    ...\n    ts[target.apk]> as                     # scan\n    ...\n    [+] done, found 6403 issues (174.94 sec.)\n    ts[target.apk]> gh report.html\n\n### Batch mode\n\nWe accept an inline command (`-c`) or script file (`-i`) to run before giving you prompt, as well as quitting right away instead of prompting (`-q`; we don't require a tty in this mode!).\n\nYou can use the features to conduct a batch scan, as follows e.g. to dump findings right onto the stderr:\n\n    $ trueseeing -eqc 'as' target.apk\n\nTo generate a report file in HTML format:\n\n    $ trueseeing -eqc 'as;gh report.html' target.apk\n\nTo generate a report file in JSON format:\n\n    $ trueseeing -eqc 'as;gj report.json' target.apk\n\nTo get report generated in stdout, omit filename from final `g*` command:\n\n    $ trueseeing -eqc 'as;gh' target.apk > report.html\n    $ trueseeing -eqc 'as;gj' target.apk > report.json\n\n### Non-interactive scan mode (deprecated)\n\nTraditionally, you can scan apps with the following command line to get findings listed in stderr:\n\n    $ trueseeing --scan target.apk\n\nTo generate a report in HTML format:\n\n    $ trueseeing --scan --scan-output report.html target.apk\n    $ trueseeing --scan --scan-report=html --scan-output report.html target.apk\n\nTo generate a report in JSON format:\n\n    $ trueseeing --scan --scan-report=json --scan-output report.json target.apk\n\nTo get report generated in stdout, specify '-' as filename:\n\n    $ trueseeing --scan --scan-output - target.apk > report.html\n    $ trueseeing --scan --scan-report=html --scan-output - target.apk > report.html\n    $ trueseeing --scan --scan-report=json --scan-output - target.apk > report.json\n\n## Advanced Usages\n\n### Extensions\n\nYou can write your own commands and signatures as extensions.  Extensions are placed under `/ext` (containers) or `~/.trueseeing2/extensions/` (pip) . Alternatively you can distribute your extensions as wheels. We provide type information so you can not only type-check your extensions with [mypy](https://github.com/python/mypy) but also get a decent assist from IDEs. See the details section for details.\n\n## Build\n\nYou can build it as follows:\n\n    $ docker build -t trueseeing https://github.com/alterakey/trueseeing.git#main\n\nTo build wheels you can do with [flit](https://flit.pypa.io/en/stable/), as follows:\n\n    $ flit build\n\nTo hack it, you need to create a proper build environment. To create one, set up a venv, install [flit](https://flit.pypa.io/en/stable/) in there, and have it pull dependencies and validating toolchains; esp. [mypy](https://github.com/python/mypy) and [pflake8](https://github.com/csachs/pyproject-flake8).  In short, do something like this:\n\n    $ git clone https://github.com/alterakey/trueseeing.git wc\n    $ python3 -m venv wc/.venv\n    $ source wc/.venv/bin/activate\n    (.venv) $ pip install flit\n    (.venv) $ flit install --deps=develop -s\n    (.venv) $ (... hack ...)\n    (.venv) $ trueseeing ...                         # to run\n    (.venv) $ mypy trueseeing && pflake8 trueseeing  # to validate\n    Success: no issues found in XX source files\n    (.venv) $ flit build                             # to build (wheel)\n    (.venv) $ docker build -t trueseeing .           # to build (container)\n\n\n## Details\n\n### Vulnerability Classes\n\nCurrently we can detect the following class of vulnerabilities, largely ones covered in OWASP Mobile Top 10 - 2016:\n\n  * Improper Platform Usage (M1)\n\n    * Debuggable\n    * Inadvent publishing of Activities, Services, ContentProviders, BroadcastReceivers\n\n  * Insecure Data (M2)\n\n    * Backupable (i.e. suspectible to the backup attack)\n    * Insecure file permissions\n    * Logging\n\n  * Insecure Commnications (M3)\n\n    * Lack of pinning (i.e. suspictible to the TLS interception attack)\n    * Use of cleartext HTTP\n    * Tamperable WebViews\n\n  * Insufficient Cryptography (M5)\n\n    * Hardcoded passphrase/secret keys\n    * Vernum ciphers with static keys\n    * Use of the ECB mode\n\n  * Client Code Quality Issues (M7)\n\n    * Reflectable WebViews (i.e. XSSs in such views should be escalatable to remote code executions via JS reflection)\n    * Usage of insecure policy on mixed contents\n\n  * Code Tampering (M8)\n\n    * Hardcoded certificates\n\n  * Reverse Engineering (M9)\n\n    * Lack of obfuscation\n\n### Extension API\n\nOur extension API lays under the `trueseeing.api` package. As we provide type information with it, your IDE will assist you when writing your extensions.\n\n#### Commands\n\nTo define new commands, implement `trueseeing.api.Command` and advertise them.\n\nThe following class will provide a sample command as `t`, for example:\n\n```python\nfrom typing import TYPE_CHECKING\nfrom trueseeing.api import Command\nfrom trueseeing.core.ui import ui\nif TYPE_CHECKING:\n  from trueseeing.api import CommandMap, CommandPatternMap, ModifierMap, OptionMap, ConfigMap\n\nclass MyCommand(Command):\n  @staticmethod\n  def create() -> Command:\n    return MyCommand()\n\n  def get_commands(self) -> CommandMap:\n    return {'t':dict(e=self._test, n='t', d='sample command')}\n\n  def get_command_patterns(self) -> CommandPatternMap:\n    return dict()\n\n  def get_modifiers(self) -> ModifierMap:\n     return dict()\n\n  def get_options(self) -> OptionMap:\n    return dict()\n\n  def get_configs(self) -> ConfigMap:\n    return dict()\n\n  async def _test(self) -> None:\n    ui.info('hello world')\n```\n\n#### Signatures\n\nTo define new signatures, implement `trueseeing.api.Signature` and advertise them.\n\nThe following class will provide a sample detector as `my-sig`, for example:\n\n```python\nfrom typing import TYPE_CHECKING\nfrom trueseeing.api import Signature\nif TYPE_CHECKING:\n  from trueseeing.api import SignatureMap, ConfigMap\n\nclass MySignature(Signature):\n  @staticmethod\n  def create() -> Signature:\n    return MySignature()\n\n  def get_sigs(self) -> SignatureMap:\n    return {'my-sig':dict(e=self._detect, d='sample signature')}\n\n  def get_configs(self) -> ConfigMap:\n    return dict()\n\n  async def _detect(self) -> None:\n    self._helper.raise_issue(\n      self._helper.build_issue(\n        sigid='my-sig',\n        title='hello world',\n        cvss='CVSS:3.0/AV:P/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:N/',\n      )\n    )\n```\n\n\n#### File Formats\n\nTo define new file formats, firstly implement `Context`s (ABC) for your formats, then implement `trueseeing.api.FileFormatHandler` to create and return their instances, and advertise them.\n\nThe following class will provide APK file support under the type named `apk2`, for example:\n\n```python\nfrom typing import TYPE_CHECKING\nfrom trueseeing.api import FileFormatHandler\nfrom trueseeing.core.android.context import APKContext\n\nif TYPE_CHECKING:\n  from typing import Optional, Set\n  from trueseeing.api import FormatMap, ConfigMap\n  from trueseeing.core.context import Context, ContextType\n\nclass MyAPKContext(APKContext):\n  # Use a different context type\n  def _get_type(self) -> Set[ContextType]:\n    return {'apk2'}\n\nclass APKFileFormatHandler(FileFormatHandler):\n  @staticmethod\n  def create() -> FileFormatHandler:\n    return APKFileFormatHandler()\n\n  def get_formats(self) -> FormatMap:\n    return {'apk2':dict(e=self._handle, r=r'\\.apk$', d='sample file format')}\n\n  def get_configs(self) -> ConfigMap:\n    return dict()\n\n  def _handle(self, path: str) -> Optional[Context]:\n    return MyAPKContext(path)\n```\n\nThen make sure you check for the type of the context in your signatures, making them ignored on unsupported contexts:\n\n```python\ncontext = self._helper.get_context().require_type('apk2')\n```\n\nUpon successful check, `require_type(...)` will try to downcast them to appropriate types for your convenience.\n\nBut by design it works only for known types (currently, the `apk`).  So if you are defining some detailed interface in your new context classes as we do for the `apk` type, you need to do a downcast here i.e.:\n\n```python\ncontext: MyAPKContext = self._helper.get_context().require_type('apk2')  # type:ignore[assignment]\n```\n\nIt is possible to define multiple formats matching the same pattern. We evaluate patterns in the order of from the most stringent (i.e. long) to the least. You use the `-F` switch to force some format to use with the target file, e.g.:\n\n```\n$ trueseeing -F apk2 target.apk\n```\n\n#### Package requirements\n\nExtensions can be either: a) any package placed under `/ext` (container) or `~/.trueseeing2/extensions` (pip), or b) any installed module named with the prefix of `trueseeing_ext0_`.\n\n### Origin of Project Name?\n\nThe D&D spell, [True Seeing](https://www.dandwiki.com/wiki/SRD:True_Seeing).\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Trueseeing is a non-decompiling Android application vulnerability scanner.",
    "version": "2.2.2",
    "project_urls": {
        "Source": "https://github.com/alterakey/trueseeing"
    },
    "split_keywords": [
        "android",
        "security",
        "pentest",
        "hacking"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "63721e61150a089eab6980c8a813297bb421ceea545b26c0c207fc4f20e75627",
                "md5": "14e08b8c36881ac58114c6e27c72582e",
                "sha256": "cfeaaa78f6af21f0ba75bf2f9858c0561d2b6e43f9c9147a3fb3b11a0b894482"
            },
            "downloads": -1,
            "filename": "trueseeing-2.2.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "14e08b8c36881ac58114c6e27c72582e",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 8852777,
            "upload_time": "2024-03-19T19:52:36",
            "upload_time_iso_8601": "2024-03-19T19:52:36.387274Z",
            "url": "https://files.pythonhosted.org/packages/63/72/1e61150a089eab6980c8a813297bb421ceea545b26c0c207fc4f20e75627/trueseeing-2.2.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "06e46d212a0e77cae21fc9aa04764466826c255057d57600c5a2ffe75f8538ba",
                "md5": "b255ce2c7e549393bff2908c94c213fb",
                "sha256": "e58db51fb4a689fb030e9ea378e90c06df6ea35433f06adaa52475166c540d0a"
            },
            "downloads": -1,
            "filename": "trueseeing-2.2.2.tar.gz",
            "has_sig": false,
            "md5_digest": "b255ce2c7e549393bff2908c94c213fb",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 8833955,
            "upload_time": "2024-03-19T19:52:44",
            "upload_time_iso_8601": "2024-03-19T19:52:44.419657Z",
            "url": "https://files.pythonhosted.org/packages/06/e4/6d212a0e77cae21fc9aa04764466826c255057d57600c5a2ffe75f8538ba/trueseeing-2.2.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-03-19 19:52:44",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "alterakey",
    "github_project": "trueseeing",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "lcname": "trueseeing"
}
        
Elapsed time: 0.26127s