
정적 및 동적 분석 도구로, 오픈소스 패키지의 악성, 취약, 위험 속성을 감사하며, 샌드박스 설치를 통해 공급망 공격을 방지합니다.
Packj (패키지로 발음)는 소프트웨어 공급망 공격을 완화하는 도구입니다. NPM, RubyGems, PyPI와 같은 인기 있는 오픈소스 패키지 레지스트리에서 악성, 취약, 유지보수 중단, 타이포스쿼팅 및 기타 "위험한" 패키지를 탐지할 수 있습니다. 노이즈를 최소화하기 위해 쉽게 사용자 정의할 수 있습니다. Packj는 박사 연구 프로젝트로 시작했으며 현재 다양한 정부 보조금으로 개발 중입니다.
참고 이번 달 말에 Packj 웹서버 및 여러 통합 기능이 제공됩니다. 👊 이 저장소를 지켜봐 주세요.

여러 배포 모델을 지원합니다:
Packj를 사용하여 풀 리퀘스트의 종속성을 감사합니다.```yaml
View on GitHub [marketplace](https://github.com/marketplace/actions/packj-security-audit). Example [PR run](https://github.com/ossillate-inc/packj-github-action-demo/pull/3#issuecomment-1274797138).
### 2. Docker 이미지 (권장)
Packj를 시도/테스트하는 가장 빠른 방법은 Docker를 사용하는 것입니다. Podman도 컨테이너화된(격리된) 실행을 위해 지원됩니다.```
docker run -v /tmp:/tmp/packj -it ossillate/packj:latest --help
이 저장소를 클론하세요,``` git clone https://github.com/ossillate-inc/packj.git && cd packj
의존성 설치```
bundle install && pip3 install -r requirements.txt
도움말로 시작하세요:``` python3 main.py --help
# 지원되는 에코시스템 #
Packj는 NPM, PyPI, Rust, PHP, Rubygems 패키지 레지스트리에서 게시된 패키지를 검증할 수 있습니다. Rust 및 PHP 지원은 진행 중입니다. 레지스트리 지원을 적극적으로 추가 중입니다.
또한 로컬(미게시) NPM 및 PyPI 패키지 검증도 지원합니다.
| 레지스트리 | 에코시스템 | 지원 여부 |
| --------- | ---------- | ------------------ |
| NPM | JavaScript | :white_check_mark: |
| PyPI | Python | :white_check_mark: |
| Cargo | Rust | :white_check_mark: |
| Rubygems | Ruby | :white_check_mark: |
| Packagist | PHP | :white_check_mark: |
| Docker | Docker | :x: |
| Nuget | .NET | :white_check_mark: |
| Maven | Java | :white_check_mark: |
| Cocoapods | Swift | :x: |
# 기능 #
Packj는 다음과 같은 도구를 제공합니다:
* [감사](#auditing-a-package) - 패키지의 "위험한" 속성을 검증합니다.
* [샌드박스](#sandboxed-package-installation) - 패키지의 안전한 설치를 위한 도구입니다.
## 패키지 감사 ##
Packj는 오픈소스 소프트웨어 패키지에서 공급망 공격에 취약하게 만드는 "위험한" 속성을 감사합니다. 예를 들어, 만료된 이메일 도메인(2FA 부족), 큰 릴리스 시간 간격, 민감한 API 또는 접근 권한 등을 가진 패키지는 위험으로 표시됩니다.
다음 감사가 지원됩니다:
- 여러 패키지: `python3 main.py audit -p pypi:requests rubygems:overcommit`
- 의존성 파일: `python3 main.py audit -f npm:package.json pypi:requirements.txt`
기본적으로 `audit`는 위험한 코드를 감지하기 위해 정적 코드 분석만 수행합니다. `-t` 또는 `--trace` 플래그를 전달하면 동적 코드 분석도 함께 수행하여 요청된 모든 패키지를 strace 아래에 설치하고 설치 시간 동작을 모니터링합니다. 아래 예제 출력을 참조하세요.
<details>
<summary><h4>예제 실행/출력 보기</h4></summary>
$ docker run -v /tmp:/tmp/packj -it ossillate/packj:latest audit --trace -p npm:browserify
[+] Fetching 'browserify' from npm..........PASS [ver 17.0.0]
[+] Checking package description.........PASS [browser-side require() the node way]
[+] Checking release history.............PASS [484 version(s)]
[+] Checking version........................RISK [702 days old]
[+] Checking release time gap............PASS [68 days since last release]
[+] Checking author.........................PASS [[email protected]]
[+] Checking email/domain validity.......RISK [expired author email domain]
[+] Checking readme.........................PASS [26838 bytes]
[+] Checking homepage.......................PASS [https://github.com/browserify/browserify#readme]
[+] Checking downloads......................PASS [2M weekly]
[+] Checking repo URL.......................PASS [https://github.com/browserify/browserify]
[+] Checking repo data...................PASS [stars: 14189, forks: 1244]
[+] Checking if repo is a forked copy....PASS [original, not forked]
[+] Checking repo description............PASS [browser-side require() the node.js way]
[+] Checking repo activity...............PASS [commits: 2290, contributors: 207, tags: 413]
[+] Checking for CVEs.......................PASS [none found]
[+] Checking dependencies...................RISK [48 found]
[+] Downloading package from npm............PASS [163.83 KB]
[+] Analyzing code..........................RISK [needs 3 perm(s): decode,codegen,file]
[+] Checking files/funcs....................PASS [429 files (383 .js), 744 funcs, LoC: 9.7K]
[+] Installing package and tracing code.....PASS [found 5 process,1130 files,22 network syscalls]
=============================================
[+] 5 risk(s) found, package is undesirable!
=> Complete report: /tmp/packj_54rbjhgm/report_npm-browserify-17.0.0_hlr1rhcz.json
{
"undesirable": [
"old package: 702 days old",
"invalid or no author email: expired author email domain",
"generates new code at runtime",
"reads files and dirs",
"forks or exits OS processes",
]
}
</details>
> 경고: 패키지가 설치 중에 악성 코드를 실행할 수 있으므로, `-t` 또는 `--trace`는 Docker 컨테이너나 가상 머신 내에서만 사용하는 것이 좋습니다.
감사는 Docker/Podman 컨테이너에서도 수행할 수 있습니다. 위험한 속성 및 사용 방법에 대한 자세한 내용은 [Audit README](https://github.com/ossillate-inc/packj/blob/main/packj/audit/README.md)를 참조하십시오.
## 샌드박스 패키지 설치 ##
Packj는 패키지의 `안전한 설치`를 위한 경량 샌드박싱을 제공합니다. 구체적으로, 악성 패키지가 민감한 데이터를 유출하거나, 민감한 파일(예: SSH 키)에 접근하거나, 악성 코드를 지속적으로 설치하는 것을 방지합니다.
설치 시간 스크립트(네이티브 컴파일 포함)를 샌드박싱합니다. **strace**를 사용합니다(즉, VM/컨테이너가 필요 없음).
샌드박싱 메커니즘 및 사용 방법에 대한 자세한 내용은 [Sandbox README](https://github.com/ossillate-inc/packj/blob/main/packj/sandbox/README.md)를 참조하십시오.
<details>
<summary><h4>예제 실행/출력 보기</h4></summary>
$ python3 main.py sandbox gem install overcommit
Fetching: overcommit-0.59.1.gem (100%)
Install hooks by running `overcommit --install` in your Git repository
Successfully installed overcommit-0.59.1
Parsing documentation for overcommit-0.59.1
Installing ri documentation for overcommit-0.59.1
#############################
# Review summarized activity
#############################
[+] Network connections
[+] DNS (1 IPv4 addresses) at port 53 [rule: ALLOW]
[+] rubygems.org (4 IPv6 addresses) at port 443 [rule: IPv6 rules not supported]
[+] rubygems.org (4 IPv4 addresses) at port 443 [rule: ALLOW]
[+] Filesystem changes
/
└── home
└── ubuntu
└── .ruby
├── gems
│ ├── iniparse-1.5.0 [new: DIR, 15 files, 46.6K bytes]
│ ├── rexml-3.2.5 [new: DIR, 77 files, 455.6K bytes]
│ ├── overcommit-0.59.1 [new: DIR, 252 files, 432.7K bytes]
│ └── childprocess-4.1.0 [new: DIR, 57 files, 141.2K bytes]
├── cache
│ ├── iniparse-1.5.0.gem [new: FILE, 16.4K bytes]
│ ├── rexml-3.2.5.gem [new: FILE, 93.2K bytes]
│ ├── childprocess-4.1.0.gem [new: FILE, 34.3K bytes]
│ └── overcommit-0.59.1.gem [new: FILE, 84K bytes]
├── specifications
│ ├── rexml-3.2.5.gemspec [new: FILE, 2.7K bytes]
│ ├── overcommit-0.59.1.gemspec [new: FILE, 1.7K bytes]
│ ├── childprocess-4.1.0.gemspec [new: FILE, 1.8K bytes]
│ └── iniparse-1.5.0.gemspec [new: FILE, 1.3K bytes]
├── bin
│ └── overcommit [new: FILE, 622 bytes]
└── doc
├── iniparse-1.5.0
│ └── ri [new: DIR, 119 files, 131.7K bytes]
├── rexml-3.2.5
│ └── ri [new: DIR, 836 files, 841K bytes]
├── overcommit-0.59.1
│ └── ri [new: DIR, 1046 files, 1.5M bytes]
└── childprocess-4.1.0
└── ri [new: DIR, 272 files, 297.8K bytes]
[C]ommit all changes, [Q|q]uit & discard changes, [L|l]ist details:
</details>
# 우리의 이야기
**요약** Packj는 박사 연구 프로젝트로 시작되었습니다. 다양한 정부 보조금을 지원받고 있습니다.
<details>
<summary><h4>자세한 설명 보기</h4></summary>
Packj는 학술 연구 프로젝트로 시작되었습니다. 구체적으로, Packj가 사용하는 정적 코드 분석 기술은 조지아텍 연구 그룹의 [MalOSS](https://github.com/osssanitizer/maloss) 프로젝트의 최첨단 사이버보안 연구를 기반으로 합니다.
<a href="https://arxiv.org/pdf/2002.01139v1.pdf" target="_blank">
<img src="https://assets.kitploit.com/production/public/readmes/5562/c4061028051de04a9aebaf4188c08fdd1bcdb47efa8a818e959d3b3fc7bc5dc7.png" width="300" alt="학술 논문">
</a>
Packj는 [NSF](https://www.sbir.gov/node/2083473), [GRA](https://gra.org/company/227/OSSPolice.html), [ALInnovate](https://innovatealabama.org)의 후한 보조금을 지원받고 있습니다.
</details>
# Packj를 선택하는 이유
**요약** 최신 취약점 스캐너는 타사 오픈소스 코드가 **선의적**이라고 가정합니다. 따라서 이러한 모든 도구는 선의적 코드의 우발적 프로그래밍 버그(예: Log4J와 같은 CVE)로 인한 위협만 처리합니다. 이들은 종속성 혼동(dependency confusion), 오타 스쿼팅(typo-squatting), 항의성 소프트웨어(protestware, 방해 행위), 계정 탈취, 사회 공학 등 공급 채널의 새로운 취약점을 이용한 악의적인 코드(악성 코드)에 의한 Solarwinds와 같은 현대 소프트웨어 공급망 공격으로부터 보호하지 **않습니다**. 최근(2022년 12월) 예로는 종속성 혼동 취약점(CVE 미할당)으로 인해 손상된 PyTorch 패키지가 있습니다.
Packj는 CVE를 감사할 뿐만 아니라, 심층적인 정적+동적 코드 분석과 메타데이터 검사를 수행하여 셸 실행, SSH 키 사용, GitHub 코드와 패키지 코드 간 불일치(출처), 2FA 부족 등과 같은 "위험한" 행동 및 속성을 감지합니다. 이러한 안전하지 않은 속성은 CVE로 분류되지 않기 때문에 기존 도구 중 어느 것도 이를 플래그할 수 없습니다. Packj는 소프트웨어 공급망의 악성, 오타 스쿼팅, 유기(abandoned), 취약 및 기타 안전하지 않은 의존성(약한 연결)을 플래그할 수 있습니다.
<details>
<summary><h4>자세한 설명 보기</h4></summary>
현재 소프트웨어 공급망 위협 모델은 타사 오픈소스 코드가 **선의적**이라고 **가정**하므로, 보안 취약성은 우발적 프로그래밍 버그(일명 CVE)에 대해서만 추적됩니다. 따라서 기존의 모든 오픈소스 취약점 스캐너는 공개적으로 알려진 CVE만 **보고**하고 선의적 코드의 우발적 버그로 인한 위협만 처리합니다.
우발적 프로그래밍 버그의 일반적인 예는 사용자 입력에 대한 범위 검사 누락으로, 코드가 버퍼 오버플로우 공격에 취약해집니다. 실제 잘 알려진 예로는 Log4J와 HeartBleed가 있습니다. 공격자는 CVE를 트리거하기 위해 익스플로잇을 개발해야 합니다(예: HeartBleed의 경우 조작된 TCP/IP 패킷 또는 버퍼 오버플로우를 유발하는 숫자로 높은 입력). CVE는 라이브러리의 새로운 버전으로 패치하거나 업그레이드하여 수정할 수 있습니다(예: Log4J의 최신 버전이 CVE를 수정함).
Solarwinds 공격 이후 현대 소프트웨어 공급망 위협 환경이 **변화**했습니다. 악의적인 행위자는 이번에는 코드가 아닌 공급 채널에서 새로운 취약점을 발견했습니다. 종속성 혼동, 오타 스쿼팅, 항의성 소프트웨어(방해 행위), 계정 탈취, 사회 공학과 같은 이러한 새로운 취약점이 악성 코드 유포에 악용되고 있습니다. 수천 개의 손상된 NPM/PyPI/Ruby 패키지가 보고되었습니다.
CVE와 대조적으로, 악성 코드는 의도적으로 나쁜(악의적인) 코드입니다. 더욱이 악성 코드 자체가 익스플로잇이며 패치하거나 최신 버전으로 업그레이드하여 수정할 수 없습니다. 예를 들어, [종속성 혼동 공격](https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610)은 의도적으로 악의적이었으며 코드 내 우발적 프로그래밍 버그를 악용하지 않았습니다. 마찬가지로, 인기 패키지의 작성자가 전쟁에 항의하기 위해 자신의 코드를 [방해](https://en.wikipedia.org/wiki/Peacenotwar)하는 것은 매우 의도적이며 어떤 CVE도 악용하지 않습니다. 오타 스쿼팅은 악의적인 행위자가 인기 있는 오픈소스 패키지 레지스트리에서 악성 코드를 유포하는 또 다른 공격 벡터입니다. 이는 코드의 우발적 프로그래밍 버그나 CVE가 아닌 [개발자의 오타와 경험 부족](https://discuss.python.org/t/improving-risks-and-consequences-against-typosquatting-on-pypi/5090)을 악용합니다.
기존 스캐너는 의도적으로 취약한(악의적인) 코드로 인한 Solarwinds와 같은 현대 소프트웨어 공급망 공격을 감지하는 데 **실패**합니다. 이러한 도구는 단순히 소스 코드를 스캔하여 오픈소스 의존성 목록을 컴파일하고 각 <의존성-이름, 의존성-버전>을 데이터베이스(예: NVD)에서 조회하여 영향을 받는 패키지 버전(예: Log4J의 취약한 버전, HeartBleed에 영향을 받는 LibSSL 버전)을 보고합니다.
Packj는 CVE를 감사할 뿐만 아니라, 심층적인 정적+동적 코드 분석과 메타데이터 검사를 수행하여 셸 실행, SSH 키 사용, GitHub 코드와 패키지 코드 간 불일치(출처), 2FA 부족 등과 같은 "위험한" 행동 및 속성을 감지합니다. 이러한 안전하지 않은 속성은 CVE로 분류되지 않기 때문에 기존 도구 중 어느 것도 이를 플래그할 수 없습니다. Packj는 소프트웨어 공급망의 악성, 오타 스쿼팅, 유기, 취약 및 기타 안전하지 않은 의존성(약한 연결)을 플래그할 수 있습니다. 자세한 내용은 [Audit README](https://github.com/ossillate-inc/packj/blob/main/packj/audit/README.md#faq)를 참조하십시오.
</details>
# 사용자 정의 #
Packj는 위협 모델에 맞게 쉽게 사용자 정의할 수 있습니다(노이즈 제로). 프로젝트/저장소의 최상위 디렉토리에 [.packj.yaml](https://github.com/ossillate-inc/packj/blob/main/.packj.yaml) 파일을 추가하고 원하지 않는 속성을 주석 처리하여 알림 피로를 줄이십시오.
# 발견된 악성 코드 #
이 도구를 사용하여 PyPI에서 40개 이상, Rubygems에서 20개 이상의 악성 패키지를 발견했습니다. 그 중 다수가 삭제되었습니다. 아래 예를 참조하십시오:
<details>
<summary><h4>악성 코드 예시 보기</h4></summary>
$ python3 main.py audit pypi:krisqian
[+] Fetching 'krisqian' from pypi...OK [ver 0.0.7]
[+] Checking version...OK [256 days old]
[+] Checking release history...OK [7 version(s)]
[+] Checking release time gap...OK [1 days since last release]
[+] Checking author...OK [[email protected]]
[+] Checking email/domain validity...OK [[email protected]]
[+] Checking readme...ALERT [no readme]
[+] Checking homepage...OK [https://www.bilibili.com/bangumi/media/md140632]
[+] Checking downloads...OK [13 weekly]
[+] Checking repo_url URL...OK [None]
[+] Checking for CVEs...OK [none found]
[+] Checking dependencies...OK [none found]
[+] Downloading package 'KrisQian' (ver 0.0.7) from pypi...OK [1.94 KB]
[+] Analyzing code...ALERT [needs 3 perms: process,network,file]
[+] Checking files/funcs...OK [9 files (2 .py), 6 funcs, LoC: 184]
=============================================
[+] 6 risk(s) found, package is undesirable!
{
"undesirable": [
"no readme",
"only 45 weekly downloads",
"no source repo found",
"generates new code at runtime",
"fetches data over the network: ['KrisQian-0.0.7/setup.py:40', 'KrisQian-0.0.7/setup.py:50']",
"reads files and dirs: ['KrisQian-0.0.7/setup.py:59', 'KrisQian-0.0.7/setup.py:70']"
]
}
=> Complete report: pypi-KrisQian-0.0.7.json
=> View pre-vetted package report at https://packj.dev/package/PyPi/KrisQian/0.0.7
</details>
Packj는 KrisQian(v0.0.7)을 소스 저장소 부재 및 패키지 설치 시간(setup.py)에 민감한 API(네트워크, 코드 생성) 사용으로 인해 의심스럽다고 플래그했습니다. 더 자세히 조사한 결과 악성 패키지임을 발견했습니다. 자세한 분석은 [https://packj.dev/malware/krisqian](https://packj.dev/malware/krisqian)에서 확인하실 수 있습니다.
우리가 발견한 악성 코드의 더 많은 예는 [https://packj.dev/malware](https://packj.dev/malware)에 나열되어 있습니다. 전체 목록은 [[email protected]](mailto:[email protected])으로 문의하시기 바랍니다.
# 자료 #
Packj 도구 또는 오픈소스 소프트웨어 공급망 공격에 대해 더 알아보려면 다음 자료를 참조하십시오.
[](https://www.youtube.com/watch?v=Rcuqn56uCDk)
[](https://www.youtube.com/watch?v=a7BfDGeW_jY)
- PyConUS'22 [발표 동영상](https://www.youtube.com/watch?v=Rcuqn56uCDk) 및 [슬라이드](https://speakerdeck.com/ashishbijlani/pyconus22-slides).
- BlackHAT Asia'22 Arsenal [발표](https://www.blackhat.com/asia-22/arsenal/schedule/#mitigating-open-source-software-supply-chain-attacks-26241)
- PackagingCon'21 [발표 동영상](https://www.youtube.com/watch?v=PHfN-NrUCoo) 및 [슬라이드](https://speakerdeck.com/ashishbijlani/mitigating-open-source-software-supply-chain-attacks)
- BlackHat USA'22 Arsenal 발표 [오타 스쿼팅, 백도어, 유기 및 기타 "위험한" 오픈소스 패키지를 Packj로 감지](https://www.blackhat.com/us-22/arsenal/schedule/#detecting-typo-squatting-backdoored-abandoned-and-other-risky-open-source-packages-using-packj-28075)
- 조지아텍 연구 그룹의 오픈소스 소프트웨어 보안에 관한 학술 [박사 논문](https://cyfi.ece.gatech.edu/publications/DUAN-DISSERTATION-2019.pdf) 및 이 연구를 시작한 [논문](https://www.ndss-symposium.org/wp-content/uploads/ndss2021_1B-1_23055_paper.pdf)
- Open Source Summit, Europe'22 발표 [오픈소스 소프트웨어 공급망의 "약한 연결"을 감지하기 위한 의존성 점수 매기기](https://osseu2022.sched.com/overview/type/SupplyChainSecurityCon) - YouTube 발표 [동영상](https://www.youtube.com/watch?v=a7BfDGeW_jY)
- NullCon'22 [Packj를 사용한 악성 및 기타 "위험한" 오픈소스 패키지 발굴](https://archive.nullcon.net/website/goa-2022/speakers/unearthing-malicious-and-other-risky-open-source-packages-using-packj.php) 발표 [동영상](https://www.youtube.com/watch?v=PgvlSjl-mrY) 및 [슬라이드](https://drive.google.com/file/d/1qLXIXzsIhRlS0mo8nwWI9KmD12CImZY9/view?usp=sharing)
# 기능 로드맵 #
* Rust 분석기 추가. Rust는 작업 진행 중입니다 [예상: 2024년 2월].
* 여러 (TODO) "위험한" 코드 및 메타데이터 속성을 감지하는 기능 추가 [예상: 2024년 2월].
* 자체 호스팅 Packj 웹 서버 및 여러 유용한 통합(예: Gitlab runner) [예상: 2024년 4월].
이 저장소를 :eyes: 워치하여 최신 소식을 받아보세요.
기능 요청이나 지원이 필요하신가요? [GitHub 토론 페이지](https://github.com/ossillate-inc/packj/discussions/)를 방문하거나 [디스코드 커뮤니티](https://discord.gg/qFcqaV2wYa)에 가입하여 논의 및 요청을 해주세요.
# 팀 및 기여자 #
Packj는 [Ossillate Inc.](https://packj.dev/team)의 사이버보안 연구원들과 외부 협력자들이 개발하여, 개발자들이 신뢰할 수 없는 타사 오픈소스 소프트웨어 의존성을 사용할 때 공급망 공격 위험을 완화할 수 있도록 돕고 있습니다. 개발자 및 협력자분들께 감사드립니다. 저희 작업이 마음에 드시면 :star:로 응원해주세요.
창립 멤버:
* Ashish Bijlani
* Devdutt Patnaik
* Ajinkya Rajput
코드 기여를 환영합니다. [CONTRIBUTING.md](https://github.com/ossillate-inc/packj/blob/HEAD/CONTRIBUTING.md) 지침을 참조하십시오. 버그를 발견하셨나요? 이슈를 열어주세요. 보안 문제 신고는 [SECURITY.md](https://github.com/ossillate-inc/packj/blob/HEAD/SECURITY.md) 지침을 참조하십시오.
# FAQ #
<details>
<summary><b>어떤 패키지 관리자(레지스트리)를 지원하나요?</b></summary>
Packj는 현재 NPM, PyPI, RubyGems 패키지의 "위험한" 속성을 검증할 수 있습니다. Rust 지원을 추가하고 있습니다.
</details>
<details>
<summary><b>Packj는 위험한/악성 패키지를 감지하기 위해 어떤 기술을 사용하나요?</b></summary>
Packj는 포괄적인 감사를 위해 정적 코드 분석, 동적 추적 및 메타데이터 분석을 사용합니다. 정적 분석만으로는 코드 난독화를 사용하여 더 잘 숨길 수 있는 정교한 악성 코드를 플래그하는 데 충분하지 않습니다. 동적 분석은 패키지를 `strace` 아래에 설치하고 런타임 동작을 모니터링하여 수행됩니다. 자세한 내용은 [Audit README](https://github.com/ossillate-inc/packj/blob/main/packj/audit/README.md)를 참조하십시오.
</details>
<details>
<summary><b>난독화된 호출에서도 작동하나요? 예를 들어, 복호화된 후 셸에 전달되는 base64 암호화 문자열 같은 경우요?</b></summary>이것은 매우 일반적인 악성 행위입니다. Packj는 코드 난독화와 셸 명령어 실행(exec 시스템 호출)을 탐지합니다. 예를 들어, Packj는 `getattr()` 및 `eval()` API 사용을 플래그할 수 있습니다. 이는 "런타임 코드 생성"을 나타내기 때문입니다. 개발자는 그때 더 자세히 살펴볼 수 있습니다. 자세한 내용은 [main.py](https://github.com/ossillate-inc/packj/blob/main/packj/audit/main.py#L512)를 참조하십시오.
</details>