
보안 연구 랩: CVE-2025-52467 (pgai pull_request_target 워크플로 코드 실행 / GITHUB_TOKEN 탈취) 재현 — timescale/pgai 스냅샷
자동화된 연구 산출물 — 업스트림 프로젝트가 아님.
이 저장소는 Université Laval의 석사 논문을 위해 자동화된 하네스가 구축한 일회용 실험실로, 게시된 GitHub Actions 워크플로우 취약점을 재현하기 위한 것입니다. 이는 커밋
2a209b058c60823b57e8b7775b10244ff340eb8b(2025-05-14) 시점의timescale/pgai의 축어적 스냅샷이며, 해당 프로젝트의 자체 라이선스 하에 재배포되며, 그 라이선스 파일은 이 스냅샷에 변경 없이 포함되어 있습니다.업스트림 프로젝트는 관여하지 않으며, 결코 대상이 되지 않고, 여기서 연구된 취약점은 이미 공개된 것입니다. 이 저장소의 모든 시크릿과 변수는 무작위로 생성된 더미 값입니다 — 실제 자격 증명은 존재하지 않습니다. 액션 참조와 러너 이미지는 2025-05-14에 확인된 값으로 고정되어 있습니다. 스냅샷에 적용된 모든 변경 사항은 하네스 출력의
pinning.md를 참조하세요.질문 또는 이의 제기: [email protected]
PostgreSQL을 RAG 및 에이전틱 애플리케이션을 위한 강력하고 프로덕션 준비가 된 검색 엔진으로 변환하는 Python 라이브러리입니다.
🔄 PostgreSQL 데이터와 S3 문서에서 벡터 임베딩을 자동으로 생성하고 동기화합니다. 데이터가 변경되면 임베딩도 자동으로 업데이트됩니다.
🔍 pgvector 및 pgvectorscale을 사용한 강력한 벡터 및 의미론적 검색.
🛡️ 기본 제공되는 프로덕션 준비 기능: 효율적인 임베딩 생성을 위한 배치 처리를 지원하며, 모델 실패, 속도 제한, 지연 시간 급증에 대한 내장 처리를 제공합니다.
🐘 Timescale Cloud, Amazon RDS, Supabase 등을 포함한 모든 PostgreSQL 데이터베이스에서 작동합니다.
기본 아키텍처: 시스템은 사용자가 작성하는 애플리케이션, PostgreSQL 데이터베이스, 그리고 무상태(stateless) 벡터라이저 워커로 구성됩니다. 애플리케이션은 PostgreSQL 또는 S3와 같은 소스의 데이터를 임베딩하기 위한 벡터라이저 구성을 정의합니다. 워커는 이 구성을 읽고, 데이터 큐를 임베딩 및 청크 분할된 텍스트로 처리한 후 결과를 다시 기록합니다. 그런 다음 애플리케이션은 이 데이터를 쿼리하여 RAG 및 의미론적 검색을 구동합니다.
이 아키텍처의 핵심 강점은 복원력에 있습니다: 애플리케이션이 수행하는 데이터 수정은 임베딩 프로세스와 분리되어 있어, 임베딩 서비스의 실패가 핵심 데이터 작업에 영향을 미치지 않도록 보장합니다.
먼저 pgai 패키지를 설치합니다.``` pip install pgai
그런 다음, pgai 데이터베이스 구성 요소를 설치합니다. 이는 터미널에서 CLI를 사용하거나 Python 애플리케이션 코드에서 pgai Python 패키지를 사용하여 수행할 수 있습니다.```
# from the cli
pgai install -d <database-url>
# or from the python package, often done as part of your application setup
import pgai
pgai.install(DB_URL)
이 빠른 시작은 pgai Vectorizer가 데이터 변경 시 임베딩을 자동으로 생성하고 동기화하여 PostgreSQL 데이터에 대한 의미론적 검색과 RAG를 어떻게 가능하게 하는지 보여줍니다.
pgai Vectorizer의 핵심 "비결"은 임베딩 생성에 대한 선언적 접근 방식입니다. 파이프라인을 정의하기만 하면 Vectorizer가 임베딩을 동기화 상태로 유지하는 운영상의 복잡성을 처리하며, 임베딩 엔드포인트가 불안정한 경우에도 마찬가지입니다. 파이프라인의 간단한 버전은 다음과 같이 정의할 수 있습니다.```sql CREATE TABLE IF NOT EXISTS wiki ( id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY, url TEXT NOT NULL, title TEXT NOT NULL, text TEXT NOT NULL )
SELECT ai.create_vectorizer( 'wiki'::regclass, loading => ai.loading_column(column_name=>'text'), destination => ai.destination_table(target_table=>'wiki_embedding_storage'), embedding => ai.embedding_openai(model=>'text-embedding-ada-002', dimensions=>'1536') )
The vectorizer will automatically create embeddings for all the rows in the
`wiki` table, and, more importantly, will keep the embeddings synced with the
underlying data as it changes. **Think of it almost like declaring an index** on
the `wiki` table, but instead of the database managing the index datastructure
for you, the Vectorizer is managing the embeddings.
## Running the quick start
**Prerequisites:**
- A PostgreSQL database ([docker instructions](https://docs.timescale.com/self-hosted/latest/install/installation-docker/)).
- An OpenAI API key (we use openai for embedding in the quick start, but you can use [multiple providers](#supported-embedding-models)).
Create a `.env` file with the following:```
OPENAI_API_KEY=<your-openai-api-key>
DB_URL=<your-database-url>
빠른 시작 예제에서 전체 python 코드와 requirements.txt를 다운로드하여 .env 파일과 같은 디렉터리에서 실행할 수 있습니다.
Pgai는 데이터베이스에 몇 가지 카탈로그 테이블과 함수가 설치되어 있어야 합니다. 이는 pgai.install 함수를 사용하여 수행되며, 데이터베이스의 ai 스키마에 필요한 구성 요소를 설치합니다.```python
pgai.install(DB_URL)
### 벡터라이저 생성
이 단계는 벡터라이저를 정의하며, 시스템이 `wiki` 테이블의 `text` 열에서 임베딩을 생성하는 방법을 알려줍니다. 벡터라이저는 `wiki_embedding` 뷰를 생성하며, 이를 통해 임베딩을 쿼리할 수 있습니다(아래에서 확인할 수 있습니다).```python
async def create_vectorizer(conn: psycopg.AsyncConnection):
async with conn.cursor() as cur:
await cur.execute("""
SELECT ai.create_vectorizer(
'wiki'::regclass,
if_not_exists => true,
loading => ai.loading_column(column_name=>'text'),
embedding => ai.embedding_openai(model=>'text-embedding-ada-002', dimensions=>'1536'),
destination => ai.destination_table(view_name=>'wiki_embedding')
)
""")
await conn.commit()
이 예시에서는 기존 데이터에 대한 임베딩을 생성하기 위해 벡터라이저 워커를 한 번 실행합니다.```python worker = Worker(DB_URL, once=True) worker.run()
실제 애플리케이션에서는 임베딩을 생성할 때마다 이렇게 수동으로 워커를 호출하지 않습니다. 대신 워커를 백그라운드에서 실행하여 벡터라이저의 작업을 지속적으로 폴링하게 합니다.
워커는 애플리케이션, CLI 또는 Docker에서 백그라운드로 실행할 수 있습니다. 자세한 내용은 [vectorizer worker](https://github.com/pvharmo2/gha-lab-e8902eccd3/blob/main/docs/vectorizer/worker.md) 문서를 참조하세요.
### 의미 검색을 사용하여 위키 문서 검색
이것은 PostgreSQL에서 표준 pgvector 의미 검색입니다. 검색은 벡터라이저가 생성한 `wiki_embedding` 뷰에 대해 수행되며, 이 뷰에는 `wiki` 테이블의 모든 열과 `embedding` 열 및 청크 텍스트가 포함됩니다. 이 함수는 `wiki` 테이블의 전체 `text` 열과 쿼리와 가장 관련성이 높은 더 작은 텍스트 청크를 모두 반환합니다.```python
@dataclass
class WikiSearchResult:
id: int
url: str
title: str
text: str
chunk: str
distance: float
async def _find_relevant_chunks(client: AsyncOpenAI, query: str, limit: int = 1) -> List[WikiSearchResult]:
# Generate embedding for the query using OpenAI's API
response = await client.embeddings.create(
model="text-embedding-ada-002",
input=query,
encoding_format="float",
)
embedding = np.array(response.data[0].embedding)
# Query the database for the most similar chunks using pgvector's cosine distance operator (<=>)
async with pool.connection() as conn:
async with conn.cursor(row_factory=class_row(WikiSearchResult)) as cur:
await cur.execute("""
SELECT w.id, w.url, w.title, w.text, w.chunk, w.embedding <=> %s as distance
FROM wiki_embedding w
ORDER BY distance
LIMIT %s
""", (embedding, limit))
return await cur.fetchall()
이 코드는 수행하지 않는 작업으로 주목할 만합니다. 이는 wiki 테이블에 새 문서를 단순히 삽입하는 것입니다. 임베딩을 생성하기 위해 별도의 작업을 할 필요가 없었으며, 벡터라이저 워커가 데이터가 변경됨에 따라 임베딩 업데이트를 처리할 것입니다.```python
def insert_article_about_pgai(conn: psycopg.AsyncConnection):
async with conn.cursor(row_factory=class_row(WikiSearchResult)) as cur:
await cur.execute("""
INSERT INTO wiki (url, title, text) VALUES
('https://en.wikipedia.org/wiki/pgai', 'pgai', 'pgai is a Python library that turns PostgreSQL into the retrieval engine behind robust, production-ready RAG and Agentic applications. It does this by automatically creating vector embeddings for your data based on the vectorizer you define.')
""")
await conn.commit()
### LLM으로 RAG 수행하기
이 코드는 LLM으로 RAG를 수행합니다. 위에서 정의한 `_find_relevant_chunks` 함수를 사용하여 `wiki` 테이블에서 가장 관련성 높은 텍스트 청크를 찾은 다음, LLM을 사용하여 응답을 생성합니다.```python
query = "What is the main thing pgai does right now?"
relevant_chunks = await _find_relevant_chunks(client, query)
context = "\n\n".join(
f"{chunk.title}:\n{chunk.text}"
for chunk in relevant_chunks
)
prompt = f"""Question: {query}
Please use the following context to provide an accurate response:
{context}
Answer:"""
response = await client.chat.completions.create({
model: "gpt-3.5-turbo",
messages: [{ role: "user", content: prompt }],
})
print("RAG response:")
print(response.choices[0].message.content)
다른 빠른 시작 가이드를 확인하세요:
vectorizer에 대해 자세히 알아보세요:
pgai Python 라이브러리를 사용하면 데이터에서 생성된 임베딩으로 작업할 수 있습니다:
벡터 임베딩을 사용하여 다음을 수행할 수 있습니다:
또한 SQL에서 직접 LLM 모델 호출을 수행할 수 있는 PostgreSQL 확장도 제공합니다. 이는 기존 데이터에 대한 분류, 요약 및 데이터 강화와 같은 사용 사례에 유용합니다.
vectorizer는 유연하고 사용자 지정이 가능하도록 설계되었습니다. 각 vectorizer는 데이터에서 임베딩을 생성하기 위한 파이프라인을 정의합니다. 파이프라인은 데이터에 순차적으로 적용되는 일련의 구성 요소로 정의됩니다:
임베딩에 지원되는 모델은 다음과 같습니다:
벡터 임베딩을 생성하는 것 자체는 쉽고 간단합니다. 문제는 LLM이 다소 불안정하고 엔드포인트가 간헐적인 오류 및/또는 성능 저하를 보인다는 점입니다. 오류를 적절히 처리하는 데 있어 중요한 부분은 기본 데이터 수정 작업(INSERT, UPDATE, DELETE)이 임베딩 작업에 의존해서는 안 된다는 것입니다. 그렇지 않으면 엔드포인트가 느리거나 실패할 때마다 애플리케이션이 중단되고 사용자 경험이 저하됩니다.
일반적으로 엔드포인트 오류를 적절히 처리하려면 사용자 지정 MLops 파이프라인을 구현해야 합니다. 여기에는 일반적으로 Kafka와 같은 큐잉 시스템, 전용 워커, 큐 처리 및 실패한 요청 재시도를 위한 기타 인프라가 포함됩니다. 이는 많은 작업이며 잘못 구현하기 쉽습니다.
pgai를 사용하면 vectorizer가 임베딩을 관리하므로 이 모든 것을 건너뛰고 애플리케이션 구축에 집중할 수 있습니다. 발생할 수 있는 다양한 오류 모드를 처리하기 위해 큐잉 및 재시도 로직을 내장했습니다. 이 작업을 백그라운드에서 수행하므로 기본 데이터 수정 작업은 임베딩 작업에 의존하지 않습니다. 이것이 pgai가 즉시 프로덕션에 사용할 수 있는 이유입니다.
많은 전문 벡터 데이터베이스가 임베딩을 생성해 줍니다. 그러나 임베딩 엔드포인트가 다운되거나 성능이 저하되면 일반적으로 실패하여 오류 처리 및 재시도의 부담이 다시 사용자에게 돌아갑니다.
pgai에 대한 기여를 환영합니다! 자세한 내용은 Contributing 페이지를 참조하세요.
pgai는 아직 초기 단계입니다. 지금이 이 프로젝트의 방향을 결정하는 데 도움을 줄 수 있는 좋은 시기입니다. 현재 우선순위를 결정 중입니다. 작업을 고려 중인 기능 목록을 살펴보세요. 언제든지 댓글을 달거나, 목록을 확장하거나, Discussions 포럼에 참여하세요.
시작하려면 기여 방법과 개발/테스트 환경 설정 방법을 살펴보세요.
Timescale은 PostgreSQL 데이터베이스 회사입니다. 자세한 내용은 timescale.com에서 확인하세요.
Timescale Cloud는 가장 까다로운 AI, 시계열, 분석 및 이벤트 워크로드를 위한 PostgreSQL 서비스를 제공하는 고성능, 개발자 중심의 클라우드 플랫폼입니다. Timescale Cloud는 프로덕션 애플리케이션에 적합하며 고가용성, 스트리밍 백업, 시간 경과에 따른 업그레이드, 역할 및 권한, 뛰어난 보안을 제공합니다.