Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions .github/ETHICS_QUESTIONNAIRE.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
**Ethics & Regulatory Questionnaire**
*This PR cannot be merged until this form is completed.*

Please reply to this comment and answer all questions below (you can copy-paste and fill it).

1. Does this change involve any of the following? (check all that apply)
- [ ] Training or fine-tuning of AI/ML models
- [ ] Inference/serving of AI/ML models in production
- [ ] Processing of personal data (PII, health, biometric, financial, children’s data, etc.)
- [ ] Dual-use or military-applicable technology
- [ ] Safety-critical systems (medical device, aviation, automotive, etc.)
- [ ] High-impact algorithmic decision-making (credit, hiring, criminal justice, etc.)
- [ ] None of the above (pure docs, tests, CI, formatting, etc.)

2. Estimated risk level (your honest assessment)
- [ ] Low – no ethical or regulatory impact
- [ ] Medium – possible fairness/privacy concerns
- [ ] High – potential for serious harm or legal non-compliance

3. Brief description of any ethical/regulatory impact (or write “None”)

>

4. Relevant regulations / standards considered (e.g., EU AI Act, GDPR, HIPAA, NIST AI RMF, export controls, etc.)
List them or write “N/A”

>

5. Have mitigation measures been implemented (bias testing, data minimization, consent flows, etc.)?
- [ ] Yes → describe below
- [ ] No
- [ ] Not applicable

>

Thank you! The ethics gate will evaluate your answers automatically.
23 changes: 23 additions & 0 deletions .github/issue_template/questionnaire.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Ethics & Regulatory Questionnaire
description: Required for all PRs with potential ethical/regulatory impact
title: "[Ethics Review] <PR title>"
body:
- type: checkboxes
attributes:
label: Scope of Change
options:
- label: Involves training or inference of AI/ML models
- label: Processes personal data (PII, health, financial, etc.)
- label: Dual-use potential (could be used in weapons/autonomous systems)
- label: Affects safety-critical systems
- label: Purely documentation / tests / CI changes (safe)

- type: textarea
attributes:
label: Description of ethical/regulatory impact (if any)
placeholder: Explain who might be harmed, fairness implications, compliance requirements, etc.

- type: dropdown
attributes:
label: Have you consulted the relevant regulatory framework?
options: ["Yes", "No", "Not applicable"]
16 changes: 10 additions & 6 deletions .github/workflows/docker-build-deploy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ jobs:
scan:
runs-on: ubuntu-latest
steps:
- name: Install presidio-analyzer
run: pip install presidio-analyzer

- name: Checkout repository
uses: actions/checkout@v2

Expand All @@ -24,15 +27,16 @@ jobs:
run: |
python -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txt

- name: Run scan
run: |
source venv/bin/activate
python main.py

- name: Upload scan report
uses: actions/upload-artifact@v2
with:
name: scan_report
path: reports/scan_report.json
##- name: Upload scan report
## uses: actions/upload-artifact@v4
## with:
## name: scan_report
## path: reports/scan_report.json
148 changes: 148 additions & 0 deletions .github/workflows/ethics-gate.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
on:
pull_request_target:
types: [opened, reopened, synchronize]
issue_comment:
types: [created]

permissions:
contents: read # needed for checkout
pull-requests: write # needed for commenting & reviews (gh) when running in pull_request_target
checks: write # needed to create check runs

jobs:
# Job that posts the questionnaire (runs in the trusted pull_request_target context).
post-questionnaire:
if: github.event_name == 'pull_request_target' && github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- name: Checkout base repo (safe; do NOT checkout PR head here)
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.sha }}
fetch-depth: 0

- name: Authenticate gh CLI with GITHUB_TOKEN
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | gh auth login --with-token

- name: Check if questionnaire already answered
id: check
run: |
PR_NUMBER=${{ github.event.pull_request.number }}
# Collect PR comments (robust to empty output)
RESPONSES=$(gh pr view "$PR_NUMBER" --json comments --jq '.comments[].body' 2>/dev/null | grep -i "Ethics & Regulatory Questionnaire" -A 20 || true)
if [[ -z "$RESPONSES" ]]; then
echo "status=missing" >> $GITHUB_OUTPUT
else
echo "status=answered" >> $GITHUB_OUTPUT
fi

- name: Post questionnaire if missing
if: steps.check.outputs.status == 'missing'
run: |
# ensure file exists in the base repo checkout (case-sensitive)
if [[ ! -f .github/ETHICS_QUESTIONNAIRE.MD ]]; then
echo ".github/ETHICS_QUESTIONNAIRE.MD not found in base repo; aborting." >&2
exit 1
fi
gh pr comment ${{ github.event.pull_request.number }} --body-file .github/ETHICS_QUESTIONNAIRE.MD
echo "Posted ethics questionnaire to PR #${{ github.event.pull_request.number }}."

# Ethics engine: collects comments, runs evaluation, posts a check, and requests changes for HIGH risk.
# This job runs in the trusted context for pull_request_target and also on issue_comment (untrusted).
# For untrusted issue_comment runs, write actions (requesting changes) may be skipped if permissions are restricted.
ethics-engine:
runs-on: ubuntu-latest
needs: post-questionnaire
steps:
- name: Checkout base repo (we run parser from base repo)
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.sha || github.ref }}
fetch-depth: 0

- name: Authenticate gh CLI with GITHUB_TOKEN
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | gh auth login --with-token

- name: Determine PR number
id: prnumber
run: |
# Determine PR number whether triggered by pull_request_target or issue_comment
PR_NUMBER=$(jq -r 'if .pull_request then .pull_request.number elif .issue then .issue.number else empty end' "$GITHUB_EVENT_PATH")
if [[ -z "$PR_NUMBER" ]]; then
echo "No PR number found in event payload; exiting."
echo "risk=UNKNOWN" >> $GITHUB_OUTPUT
exit 0
fi
echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT

- name: Collect comments
id: collect
run: |
PR=${{ steps.prnumber.outputs.pr_number }}
# Gather all PR comments into a single string (robust to empty)
ANSWERS=$(gh pr view "$PR" --json comments --jq '[.comments[].body] | join("\n\n")' 2>/dev/null || true)
echo "$ANSWERS" > answers.txt
# Expose the answers (trim to avoid huge output)
echo "answers=$(echo "$ANSWERS" | head -c 32768 | sed -e 's/"/'"'"'"/g')" >> $GITHUB_OUTPUT

- name: Run ethics parser & evaluator (safe runs code from base repo)
id: run_engine
env:
PR_NUMBER: ${{ steps.prnumber.outputs.pr_number }}
run: |
# Ensure parser exists
if [[ ! -f .github/workflows/parse_and_evaluate.py ]]; then
echo "Parser .github/workflows/parse_and_evaluate.py not found in base repo; aborting."
echo "RISK_LEVEL=UNKNOWN" > result.txt
else
python3 .github/workflows/parse_and_evaluate.py "$(cat answers.txt)" > result.txt || true
fi
cat result.txt
# Extract RISK_LEVEL=XYZ from result.txt if present
RISK=$(grep -m1 '^RISK_LEVEL=' result.txt | cut -d= -f2 || echo "LOW")
echo "risk=$RISK" >> $GITHUB_OUTPUT

- name: Create/update "Ethics Review" check run
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const risk = "${{ steps.run_engine.outputs.risk }}".trim();
const conclusions = {
"LOW": "success",
"MEDIUM": "action_required",
"HIGH": "failure"
};
const conclusion = conclusions[risk] || "failure";
const head_sha = (context.payload.pull_request && context.payload.pull_request.head && context.payload.pull_request.head.sha) || (context.payload.issue && context.payload.issue.pull_request && context.payload.issue.number ? undefined : undefined) || github.event.pull_request?.head?.sha;
await github.rest.checks.create({
owner: context.repo.owner,
repo: context.repo.repo,
name: "Ethics Review",
head_sha: head_sha || context.sha,
status: "completed",
conclusion,
output: {
title: risk === "LOW" ? "Ethics cleared" : `Ethics review: ${risk}`,
summary: risk === "LOW" ? "Low risk – automatically approved" : `Risk level ${risk} – review required`
}
});

- name: Request changes on HIGH risk (trusted-only; skip on untrusted events)
if: steps.run_engine.outputs.risk == 'HIGH'
run: |
PR=${{ steps.prnumber.outputs.pr_number }}
# Only attempt to request changes when running in pull_request_target context (trusted).
if [[ "${GITHUB_EVENT_NAME}" != "pull_request_target" ]]; then
echo "Not in pull_request_target context; skipping request-changes (insufficient permissions for fork PRs)."
exit 0
fi
# Request changes using gh (GITHUB_TOKEN from pull_request_target has write rights)
gh pr review "$PR" --request-changes -b "@ethics-team Required manual review for high-risk change"
echo "Requested changes on PR #$PR due to HIGH risk."

- name: Final status message
run: |
echo "Ethics engine completed. Risk level: ${{ steps.run_engine.outputs.risk }}"
38 changes: 38 additions & 0 deletions .github/workflows/redeengine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import os
import json
import sys

def evaluate_risk(answers):
risk_score = 0
flags = []

if answers.get("involves_ai", False):
risk_score += 3
flags.append("AI/ML component")
if answers.get("processes_pii", False):
risk_score += 5
flags.append("Personal data")
if answers.get("dual_use", False):
risk_score += 10
flags.append("🚨 Dual-use technology")
if answers.get("safety_critical", False):
risk_score += 8
flags.append("Safety-critical")

if "purely documentation" in answers.get("safe_changes", []):
return "LOW", "No ethical concerns detected."

if risk_score >= 10:
return "HIGH", " | ".join(flags)
elif risk_score >= 5:
return "MEDIUM", " | ".join(flags)
else:
return "LOW", "Minor changes"

# Parse comment or form submission here (simplified)
# In real use, you'd parse the actual comment body
answers = json.loads(sys.argv[1]) # passed from workflow
level, reason = evaluate_risk(answers)

print(f"RISK_LEVEL={level}")
print(f"REASON={reason}")
Binary file modified README.md
Binary file not shown.
20 changes: 20 additions & 0 deletions asset-scanner/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# PII & Secrets Scanner - do not commit files
scan_report.json
scan_report.local.json
scan_report.shareable.json
*.local.json
local_scan_*.json
temp_report_*.json

# ignore any backup or temp reports
*.json.bak
*.json.tmp

# Optional: ignore the raw findings before enrichment (if you ever dump them)
raw_findings.json
debug_scan.json

# OS / editor
.DS_Store
Thumbs.db
*.log
Loading