Skip to content

Commit 6bbf3f4

Browse files
author
Omur
committed
1 parent 664de35 commit 6bbf3f4

File tree

198 files changed

+41477
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

198 files changed

+41477
-0
lines changed

python/capital/CONTRIBUTING.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# How to contribute to c{api}tal?
2+
3+
c{api}tal is an open source project. As such, all contributions and suggestions are welcome.
4+
You can contribute in many ways: giving ideas, answering questions, reporting bugs, proposing enhancements,
5+
improving the documentation, fixing bugs, etc...
6+
Many thanks in advance for every contribution.
7+
8+
In order to facilitate healthy, constructive behavior in an open and inclusive community,
9+
we all respect and abide by our code of conduct.
10+
11+
## How to work on an open Issue?
12+
13+
The list of open issues can be found at: `https://github.com/Checkmarx/capital/issues`
14+
If you would like to work on any of the open Issues:
15+
- Make sure it is not already assigned to someone else.
16+
The assignee (if any) can be found on the top of the right column of the Issue page.
17+
- You can self-assign it by commenting on the Issue page with one of the keywords: `#take` or `#self-assign`.
18+
- Work on your self-assigned issue and eventually create a Pull Request.
19+
20+
21+
## How to create a Pull Request?
22+
23+
1. Fork the repository
24+
2. Clone your fork
25+
3. Create a new branch to hold your development changes:
26+
4. Develop the features in your branch
27+
5. Once you're happy with your code, add your changes and make a commit to record your changes locally
28+
6. Push the changes
29+
7. Click on `"Pull request"` to send it to the project maintainers for review

python/capital/Dockerfile

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
FROM python:3.9.10-slim
2+
COPY app /capital/app
3+
COPY requirements.txt /capital
4+
COPY alembic.ini /capital
5+
COPY main.py /capital
6+
WORKDIR /capital
7+
RUN apt update -y
8+
RUN apt install -y procps
9+
RUN pip install -r requirements.txt
10+
CMD ["python3", "main.py"]

python/capital/LICENSE

Lines changed: 663 additions & 0 deletions
Large diffs are not rendered by default.

python/capital/README.md

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
<p align="center">
2+
<img src=".github/assets/capital-logo-white.PNG#center" width="600" height="300" />
3+
</p>
4+
5+
[![API Security Top 10](https://img.shields.io/badge/API%20Security-top%2010-blue)](https://github.com/OWASP/API-Security)
6+
[![Vulnerable](https://img.shields.io/badge/Vulnerable-by%20design-blue)](https://github.com/Checkmarx/capital#quickstart)
7+
[![license](https://img.shields.io/badge/license-AGPL%20V3-blue)](https://opensource.org/licenses/AGPL-3.0)
8+
9+
Quick facts
10+
----------
11+
12+
- **Name**: 'c{api}tal'
13+
- **Type**: Vulnerable API Security application
14+
- **Purpose**: Educational
15+
- **License**: GNU AFFERO GENERAL PUBLIC LICENSE
16+
- **Language**: Python, JS
17+
- **Author**: Checkmarx Research team
18+
19+
Description
20+
----------
21+
The Checkmarx research team created c{api}tal to provide users with an active playground in which they hone their API Security skills. <br> The c{api}tal application contains 10 API challenges which map to the <a href="https://owasp.org/www-project-api-security/" target="_blank">OWASP top 10 API risks</a>. <br> It is built with Python (FastAPI) and JS (React).
22+
23+
c{api}tal can also be used for conducting your own API Security CTF event.
24+
25+
Features:
26+
----------
27+
Contains 10 challenges based on the <a href="https://owasp.org/www-project-api-security/" target="_blank">OWASP top 10 API risks</a>
28+
29+
* Built on FastAPI (backend) and React (frontend)
30+
* UI - Blogging website (i.e medium)
31+
* OpenAPI3 API JSON specification file that can be imported as a POSTMAN collection
32+
* JWT token based authentication (lifetime can be adjusted in app)
33+
34+
35+
c{api}tal is a blogging application which allow users to register, create and delete posts,
36+
create and delete comments, follow other users, and more.
37+
38+
<p align="center">
39+
<img src="postman/API%20endpoints.PNG#center" width="1000" height="850" />
40+
</p>
41+
42+
# Quickstart
43+
44+
Run the full application using docker-compose:
45+
46+
docker-compose up -d
47+
48+
49+
The backend will be running on http://localhost:8000/ <br>
50+
The frontend will be running on http://localhost:4100/ <br>
51+
Check out the API endpoints specification page at http://localhost:8000/docs <br>
52+
53+
Generate API requests to http://localhost:8000/api (via POSTMAN/Burp for example) <br>
54+
Import the API collection JSON file to POSTMAN and start generating API requests: <br>
55+
[click here to download the c{api}tal API json collection file](https://www.capital-ctf.com/files/de1ad03a48959f38c7f131f81f95d42e/capital.postman_collection.json)
56+
57+
<p align="center">
58+
<img src=".github/assets/postman%20-%20register%20user%20request.PNG#center" width="1000" height="300" />
59+
</p>
60+
61+
To run the web application in debug:
62+
----------
63+
64+
First, run ``PostgreSQL``, set environment variables and create database:
65+
66+
export POSTGRES_DB=rwdb POSTGRES_PORT=5432 POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres
67+
docker run --name pgdb --rm -p 5432:5432 -e POSTGRES_USER="$POSTGRES_USER" -e POSTGRES_PASSWORD="$POSTGRES_PASSWORD" -e POSTGRES_DB="$POSTGRES_DB" postgres
68+
export POSTGRES_HOST=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' pgdb)
69+
createdb --host=$POSTGRES_HOST --port=$POSTGRES_PORT --username=$POSTGRES_USER $POSTGRES_DB
70+
71+
[Option 1] Run locally
72+
73+
Then run the following commands to bootstrap your environment:
74+
75+
git clone https://github.com/Checkmarx/capital
76+
cd capital
77+
pip install -r requirements.txt
78+
79+
80+
Then create ``.env`` file in project root and set environment variables for application:
81+
82+
export POSTGRES_DB=rwdb POSTGRES_PORT=5432 POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres
83+
export POSTGRES_HOST=localhost
84+
export DATABASE_URL=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST:$POSTGRES_PORT/$POSTGRES_DB
85+
touch .env
86+
echo APP_ENV=dev
87+
echo DATABASE_URL=postgresql://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST:$POSTGRES_PORT/$POSTGRES_DB >> .env
88+
echo SECRET_KEY=$(openssl rand -hex 32) >> .env
89+
90+
Then run the backend server:
91+
92+
python3
93+
.py
94+
95+
[Option 2] Run backend using docker
96+
Run the backend using docker build:
97+
98+
docker build . -t capital
99+
docker run -p 8000:8000 -e DATABASE_URL=postgresql://postgres:postgres@host.docker.internal:5432/rwdb --rm --name backend-capital capital
100+
101+
102+
Run tests
103+
---------
104+
105+
Tests for this project are defined in the ``tests/`` folder.
106+
107+
Set up environment variable ``DATABASE_URL`` or set up ``database_url`` in ``app/core/settings/test.py``
108+
109+
This project uses `pytest
110+
<https://docs.pytest.org/>`_ to define tests because it allows you to use the ``assert`` keyword with good formatting for failed assertations.
111+
112+
113+
To run all the tests of a project, simply run the ``pytest`` command: ::
114+
115+
$ pytest
116+
================================================= test session starts ==================================================
117+
platform linux -- Python 3.8.3, pytest-5.4.2, py-1.8.1, pluggy-0.13.1
118+
rootdir: /home/some-user/user-projects/fastapi-realworld-example-app, inifile: setup.cfg, testpaths: tests
119+
plugins: env-0.6.2, cov-2.9.0, asyncio-0.12.0
120+
collected 90 items
121+
122+
tests/test_api/test_errors/test_422_error.py . [ 1%]
123+
tests/test_api/test_errors/test_error.py . [ 2%]
124+
tests/test_api/test_routes/test_articles.py ................................. [ 38%]
125+
tests/test_api/test_routes/test_authentication.py .. [ 41%]
126+
tests/test_api/test_routes/test_comments.py .... [ 45%]
127+
tests/test_api/test_routes/test_login.py ... [ 48%]
128+
tests/test_api/test_routes/test_profiles.py ............ [ 62%]
129+
tests/test_api/test_routes/test_registration.py ... [ 65%]
130+
tests/test_api/test_routes/test_tags.py .. [ 67%]
131+
tests/test_api/test_routes/test_users.py .................... [ 90%]
132+
tests/test_db/test_queries/test_tables.py ... [ 93%]
133+
tests/test_schemas/test_rw_model.py . [ 94%]
134+
tests/test_services/test_jwt.py ..... [100%]
135+
136+
============================================ 90 passed in 70.50s (0:01:10) =============================================
137+
$
138+
139+
If you want to run a specific test, you can do this with `this
140+
<https://docs.pytest.org/en/latest/usage.html#specifying-tests-selecting-tests>`_ pytest feature: ::
141+
142+
$ pytest tests/test_api/test_routes/test_users.py::test_user_can_not_take_already_used_credentials
143+
144+
Web routes
145+
----------
146+
147+
All routes are available on ``/docs`` or ``/redoc`` paths with Swagger or ReDoc.
148+
149+
150+
Project structure
151+
-----------------
152+
153+
Files related to application are in the ``app`` or ``tests`` directories.
154+
Application parts are:
155+
156+
app
157+
├── api - web related stuff.
158+
│   ├── dependencies - dependencies for routes definition.
159+
│   ├── errors - definition of error handlers.
160+
│   └── routes - web routes.
161+
├── core - application configuration, startup events, logging.
162+
├── db - db related stuff.
163+
│   ├── migrations - manually written alembic migrations.
164+
│   └── repositories - all crud stuff.
165+
├── models - pydantic models for this application.
166+
│   ├── domain - main models that are used almost everywhere.
167+
│   └── schemas - schemas for using in web routes.
168+
├── resources - strings that are used in web responses.
169+
├── services - logic that is not just crud related.
170+
├── credentials - list of common strings for Brute Force.
171+
├── postman - api json file for postman.
172+
├── redis - redis docker file and conf file.
173+
├── scripts
174+
├── tests
175+
└── main.py - FastAPI application creation and configuration.
176+
177+
Presented At
178+
----------
179+
180+
[Blackhat Europe 2022 Arsenal](https://www.youtube.com/watch?v=OP4X8Sc8hMs)
181+
182+
[AppSec village at DefCon30](https://www.appsecvillage.com/events/dc-2022/c%7Bapi%7Dtal-api-security-ctf)
183+
184+
Write-ups & Referrences
185+
----------
186+
[c{api}tal CTF event sum-up blog](https://checkmarx.com/blog/how-we-created-an-api-security-ctf)
187+
188+
A great write-up by Maor Tal: <br>
189+
[Part 1](https://medium.com/@maor_59001/defcon-30-appsec-villiage-ctf-writeup-part-1-1730de791f50) <br>
190+
[Part 2](https://medium.com/@maor_59001/defcon-30-c-api-tal-ctf-writeup-part-2-ef99a0fc8d28)
191+
192+
193+
Stickers from DefCon30: <br>
194+
<img src=".github/assets/sticker1.png" width="300" height="150" />
195+
<img src=".github/assets/sticker2.png" width="300" height="150" />
196+
<img src=".github/assets/sticker3.jpeg" width="300" height="150" />
197+
198+
199+
Development and Bugs
200+
----------
201+
Found an issue, or have a great idea? Let us know:
202+
203+
* E-mail - capital@checkmarx.com
204+
205+
Contributions are appreciated and can be done via GitHub.
206+
207+
See CONTRIBUTING.md for more information about how to submit them.
208+
209+
Thanks
210+
----------
211+
212+
This project was created at Checkmarx by [Ravid Mazon](https://www.linkedin.com/in/ravid-mazon) with the help of these great contributors:
213+
[Liad Levy](https://www.linkedin.com/in/liad-levy-93b235211/),
214+
[Yaniv Nizry](https://www.linkedin.com/in/yaniv-n-8b4a76193/),
215+
[Guy Lyuboshits](https://www.linkedin.com/in/guy-lyuboshits-075175165/)
216+
217+
The application was built base on ``real-world-app`` , we used these awesome repos: <br>
218+
<a href="https://github.com/nsidnev/fastapi-realworld-example-app" target="_blank">Backend - FastAPI (Python)</a> <br>
219+
<a href="https://github.com/khaledosman/react-redux-realworld-example-app" target="_blank">Frontend - React (JS)</a> <br>
220+
Thanks again for contributing to the open-source community! <br>

python/capital/alembic.ini

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
[alembic]
2+
script_location = ./app/db/migrations
3+
4+
[loggers]
5+
keys = root,sqlalchemy,alembic
6+
7+
[handlers]
8+
keys = console
9+
10+
[formatters]
11+
keys = generic
12+
13+
[logger_root]
14+
level = WARN
15+
handlers = console
16+
qualname =
17+
18+
[logger_sqlalchemy]
19+
level = WARN
20+
handlers =
21+
qualname = sqlalchemy.engine
22+
23+
[logger_alembic]
24+
level = INFO
25+
handlers =
26+
qualname = alembic
27+
28+
[handler_console]
29+
class = StreamHandler
30+
args = (sys.stderr,)
31+
level = NOTSET
32+
formatter = generic
33+
34+
[formatter_generic]
35+
format = %(levelname)-5.5s [%(name)s] %(message)s
36+
datefmt = %H:%M:%S

python/capital/app/__init__.py

Whitespace-only changes.

python/capital/app/api/__init__.py

Whitespace-only changes.

python/capital/app/api/dependencies/__init__.py

Whitespace-only changes.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from typing import Optional
2+
3+
from fastapi import Depends, HTTPException, Path, Query
4+
from starlette import status
5+
6+
from app.api.dependencies.authentication import get_current_user_authorizer
7+
from app.api.dependencies.database import get_repository
8+
from app.db.errors import EntityDoesNotExist
9+
from app.db.repositories.articles import ArticlesRepository
10+
from app.models.domain.articles import Article
11+
from app.models.domain.users import User
12+
from app.models.schemas.articles import (
13+
DEFAULT_ARTICLES_LIMIT,
14+
DEFAULT_ARTICLES_OFFSET,
15+
ArticlesFilters,
16+
)
17+
from app.resources import strings
18+
from app.services.articles import check_user_can_modify_article
19+
20+
21+
def get_articles_filters(
22+
tag: Optional[str] = None,
23+
author: Optional[str] = None,
24+
favorited: Optional[str] = None,
25+
limit: int = Query(DEFAULT_ARTICLES_LIMIT, ge=1),
26+
offset: int = Query(DEFAULT_ARTICLES_OFFSET, ge=0),
27+
) -> ArticlesFilters:
28+
return ArticlesFilters(
29+
tag=tag,
30+
author=author,
31+
favorited=favorited,
32+
limit=limit,
33+
offset=offset,
34+
)
35+
36+
37+
async def get_article_by_slug_from_path(
38+
slug: str = Path(..., min_length=1),
39+
user: Optional[User] = Depends(get_current_user_authorizer(required=False)),
40+
articles_repo: ArticlesRepository = Depends(get_repository(ArticlesRepository)),
41+
) -> Article:
42+
try:
43+
return await articles_repo.get_article_by_slug(slug=slug, requested_user=user)
44+
except EntityDoesNotExist:
45+
raise HTTPException(
46+
status_code=status.HTTP_404_NOT_FOUND,
47+
detail=strings.ARTICLE_DOES_NOT_EXIST_ERROR,
48+
)
49+
50+
51+
def check_article_modification_permissions(
52+
current_article: Article = Depends(get_article_by_slug_from_path),
53+
user: User = Depends(get_current_user_authorizer()),
54+
) -> None:
55+
if not check_user_can_modify_article(current_article, user):
56+
raise HTTPException(
57+
status_code=status.HTTP_403_FORBIDDEN,
58+
detail=strings.USER_IS_NOT_AUTHOR_OF_ARTICLE,
59+
)

0 commit comments

Comments
 (0)