CtrlK
BlogDocsLog inGet started
Tessl Logo

guydemo/authguard

Authorization and access control security guidance based on Project CodeGuard — covers RBAC/ABAC/ReBAC, IDOR prevention, mass assignment, and transaction authorization

87

1.45x
Quality

82%

Does it follow best practices?

Impact

93%

1.45x

Average score across 6 eval scenarios

SecuritybySnyk

Passed

No findings from the security scan

Overview
Quality
Evals
Security
Files

task.mdevals/scenario-1/

Content Collaboration Platform Access Control

Problem/Feature Description

A startup has built a content collaboration tool where users belong to teams and teams own collections of articles. The original access control was simple: admins can do anything, editors can edit, readers can view. As the product has grown, this has broken down — a contractor who is a reader for one team is accidentally able to view private articles owned by a completely different team, and a paying subscriber who upgrades to "premium" should unlock additional capabilities that a free reader with the same role label cannot access.

The engineering team has realized their flat role-based system can't express the real rules of who should see what: access depends not just on the user's label, but on which team they belong to, whether a specific article is marked sensitive, and the user's subscription tier. The backend lead wants the access control refactored to properly handle these nuances before rolling out the new premium tier, and wants the design documented so future engineers understand the intent.

Output Specification

Produce content_api.py containing the refactored Flask application with improved access control logic. Also produce security_notes.md explaining the authorization design decisions, including which access control approach you used and why you chose it.

Input Files

The following files are provided as inputs. Extract them before beginning.

=============== FILE: inputs/content_api.py =============== from flask import Flask, jsonify, request, abort, g

app = Flask(name)

Simulated auth — reads identity from headers (in production: JWT)

def get_current_user(): return { "user_id": int(request.headers.get("X-User-Id", "1")), "role": request.headers.get("X-Role", "reader"), # admin | editor | reader "team_id": int(request.headers.get("X-Team-Id", "1")), "tier": request.headers.get("X-Tier", "free"), # free | premium }

Simulated DB

_teams = { 1: {"id": 1, "name": "Marketing"}, 2: {"id": 2, "name": "Engineering"}, }

_articles = { 1: {"id": 1, "team_id": 1, "title": "Q2 Campaign", "sensitive": False, "content": "..."}, 2: {"id": 2, "team_id": 1, "title": "Budget Plan", "sensitive": True, "content": "..."}, 3: {"id": 3, "team_id": 2, "title": "API Roadmap", "sensitive": False, "content": "..."}, 4: {"id": 4, "team_id": 2, "title": "Security Audit", "sensitive": True, "content": "..."}, }

Simple role-permission map (current approach — known to be insufficient)

PERMISSIONS = { "admin": ["articles:read", "articles:write", "articles:delete", "articles:export"], "editor": ["articles:read", "articles:write"], "reader": ["articles:read"], }

@app.route("/api/articles/int:article_id", methods=["GET"]) def get_article(article_id): user = get_current_user() if "articles:read" not in PERMISSIONS.get(user["role"], []): abort(403) article = _articles.get(article_id) if article is None: abort(404) return jsonify(article)

@app.route("/api/articles/int:article_id", methods=["PUT"]) def update_article(article_id): user = get_current_user() if "articles:write" not in PERMISSIONS.get(user["role"], []): abort(403) article = _articles.get(article_id) if article is None: abort(404) article.update(request.get_json()) return jsonify(article)

@app.route("/api/articles/int:article_id/export", methods=["POST"]) def export_article(article_id): user = get_current_user() if "articles:export" not in PERMISSIONS.get(user["role"], []): abort(403) article = _articles.get(article_id) if article is None: abort(404) return jsonify({"url": f"https://cdn.example.com/articles/{article_id}.pdf"})

@app.route("/api/articles/int:article_id", methods=["DELETE"]) def delete_article(article_id): user = get_current_user() if user["role"] != "admin": abort(403) if article_id not in _articles: abort(404) del _articles[article_id] return "", 204

if name == "main": app.run(debug=True) =============== END FILE ===============

evals

scenario-1

criteria.json

task.md

SKILL.md

tile.json