Automatically create Linear tickets from Semgrep Pro security findings. This containerized application receives webhooks from Semgrep and creates well-formatted issues in your Linear workspace.
Note: This is an unofficial tool, provided as-is. If you have improvements to share, feel free to collaborate!
- π― Automatic ticket creation from Semgrep findings
- π§ Setup Wizard - beautiful GUI that guides you through configuration
- π Auto-tunnel for local dev - built-in ngrok integration for local testing
- π Webhook signature verification for security
- π Severity-based prioritization (Critical/High β Urgent, Medium β High, etc.)
- π Duplicate detection prevents creating multiple tickets for the same finding
- π Status dashboard with real-time activity log
- π Production ready - authentication, rate limiting, metrics, structured logging
git clone https://github.com/r2c-cse/semgrep-linear-integration.git
cd semgrep-linear-integrationdocker-compose up -dGo to http://localhost:8080/setup in your browser.
The wizard will guide you through:
| Step | What You'll Do |
|---|---|
| 1οΈβ£ | Set up ngrok tunnel - Get a public URL for local testing |
| 2οΈβ£ | Enter Linear API key - Validates and fetches your teams |
| 3οΈβ£ | Select your team - Choose where issues will be created |
| 4οΈβ£ | Select a project (optional) - Assign issues to a specific project |
| 5οΈβ£ | Configure security - Set webhook secret |
| 6οΈβ£ | Done! - Copy your webhook URL for Semgrep |
- Go to Semgrep AppSec Platform β Settings β Integrations
- Click Add β Select Webhook
- Paste the webhook URL from the setup wizard
- Click Subscribe
For production use, use the production-ready configuration:
# Create your production .env file
cat > .env << 'EOF'
# Required
LINEAR_API_KEY=lin_api_your_key_here
LINEAR_TEAM_ID=your_team_id
SEMGREP_WEBHOOK_SECRET=your_webhook_secret
# Authentication (choose at least one)
DASHBOARD_API_KEY=slw_your_generated_api_key
# Or use basic auth:
# DASHBOARD_USERNAME=admin
# DASHBOARD_PASSWORD=secure_password_here
EOF
# Start in production mode
docker-compose -f docker-compose.prod.yml up -d# Example Kubernetes deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: semgrep-linear
spec:
replicas: 2
selector:
matchLabels:
app: semgrep-linear
template:
spec:
containers:
- name: semgrep-linear
image: your-registry/semgrep-linear:latest
ports:
- containerPort: 8080
env:
- name: PRODUCTION
value: "true"
- name: LINEAR_API_KEY
valueFrom:
secretKeyRef:
name: semgrep-linear-secrets
key: linear-api-key
# ... other env vars from secrets
livenessProbe:
httpGet:
path: /health
port: 8080
readinessProbe:
httpGet:
path: /ready
port: 8080# Build and push to ECR
aws ecr get-login-password | docker login --username AWS --password-stdin $ECR_URL
docker build -t semgrep-linear .
docker tag semgrep-linear:latest $ECR_URL/semgrep-linear:latest
docker push $ECR_URL/semgrep-linear:latest
# Deploy via ECS Task Definition with environment variablesgcloud builds submit --tag gcr.io/$PROJECT_ID/semgrep-linear
gcloud run deploy semgrep-linear \
--image gcr.io/$PROJECT_ID/semgrep-linear \
--set-env-vars "PRODUCTION=true,LINEAR_API_KEY=$LINEAR_API_KEY,..."az containerapp create \
--name semgrep-linear \
--resource-group mygroup \
--image your-registry/semgrep-linear:latest \
--env-vars "PRODUCTION=true" "LINEAR_API_KEY=secretref:linear-key"| Requirement | Description | How to Configure |
|---|---|---|
| β HTTPS | Always use TLS in production | Use a reverse proxy (nginx, traefik) or cloud load balancer |
| β Webhook Secret | Verify requests are from Semgrep | Set SEMGREP_WEBHOOK_SECRET |
| β Dashboard Auth | Protect the UI from unauthorized access | Set DASHBOARD_API_KEY or DASHBOARD_USERNAME/DASHBOARD_PASSWORD |
| β Rate Limiting | Prevent abuse | Enabled by default (60 req/min) |
| β Non-root | Container runs as unprivileged user | Built into Dockerfile |
| β Health Checks | Monitor application health | Use /health and /ready endpoints |
| Variable | Description |
|---|---|
LINEAR_API_KEY |
Your Linear personal API key (starts with lin_api_) |
LINEAR_TEAM_ID |
Team ID where issues are created |
SEMGREP_WEBHOOK_SECRET |
Secret for verifying webhook signatures |
| Variable | Description |
|---|---|
DASHBOARD_API_KEY |
API key for dashboard access (recommended) |
DASHBOARD_USERNAME |
Basic auth username (alternative) |
DASHBOARD_PASSWORD |
Basic auth password (alternative) |
| Variable | Default | Description |
|---|---|---|
LINEAR_PROJECT_ID |
- | Project to assign issues to |
LINEAR_DEFAULT_PRIORITY |
2 |
Default priority (1=Urgent to 4=Low) |
PRODUCTION |
false |
Enable production mode |
PORT |
8080 |
Server port |
DEBUG |
false |
Enable debug logging |
| Variable | Default | Description |
|---|---|---|
LOG_LEVEL |
INFO |
DEBUG, INFO, WARNING, ERROR |
LOG_FORMAT |
text |
json for production, text for dev |
LOG_FILE |
- | Path to log file (also logs to stdout) |
ACTIVITY_LOG_FILE |
- | Path to persist activity history |
| Variable | Default | Description |
|---|---|---|
RATE_LIMIT_PER_MINUTE |
60 |
Max webhook requests per minute per IP |
RATE_LIMIT_BURST |
10 |
Burst allowance |
| Variable | Default | Description |
|---|---|---|
LINEAR_API_TIMEOUT |
30 |
Request timeout in seconds |
LINEAR_API_RETRIES |
3 |
Retry attempts on failure |
LINEAR_API_RETRY_DELAY |
1.0 |
Delay between retries |
| Variable | Description |
|---|---|
LOCAL_DEV |
Set to true to enable ngrok tunnel |
NGROK_AUTHTOKEN |
Your ngrok auth token |
| Endpoint | Method | Description |
|---|---|---|
/webhook |
POST | Semgrep webhook receiver |
/ping |
GET | Simple connectivity test |
/health |
GET | Health check (for load balancers) |
/ready |
GET | Readiness probe (fully configured) |
/metrics |
GET | Prometheus-compatible metrics |
| Endpoint | Method | Description |
|---|---|---|
/ |
GET | Status dashboard |
/dashboard |
GET | Dashboard (always accessible) |
/setup |
GET | Setup wizard |
/api/activity |
GET | Activity log |
/api/teams |
GET | List Linear teams |
/api/projects/<team_id> |
GET | List projects |
With API Key:
curl -H "X-API-Key: your_api_key" http://localhost:8080/dashboardWith Basic Auth:
curl -u username:password http://localhost:8080/dashboardIn Browser:
- Add
?api_key=your_keyto URL, OR - Browser will prompt for username/password
The /metrics endpoint exposes:
semgrep_linear_activities_total 42
semgrep_linear_issues_created_total 15
semgrep_linear_issues_skipped_total 5
semgrep_linear_webhooks_received_total 20
semgrep_linear_errors_total 2
semgrep_linear_up 1
| Endpoint | Use Case |
|---|---|
/health |
Kubernetes liveness probe |
/ready |
Kubernetes readiness probe |
/ping |
Simple uptime check |
With LOG_FORMAT=json:
{"timestamp": "2024-01-15T10:30:00", "level": "INFO", "logger": "app.main", "message": "Webhook received from 10.0.0.1"}- Check logs:
docker-compose logs -f - Verify webhook is enabled in Semgrep
- Check the activity log in the dashboard
- Ensure findings are new (duplicates are skipped)
- Ensure
SEMGREP_WEBHOOK_SECRETmatches Semgrep's configuration - Or leave it empty to disable signature verification (not recommended)
- Increase
RATE_LIMIT_PER_MINUTEif legitimate traffic is high - Check for duplicate webhook deliveries from Semgrep
- Verify your API key is valid
- Check if the team/project IDs are correct
- The app will retry failed requests automatically
MIT License - feel free to use and modify!
Made with β€οΈ for security teams