dumbtv
dumbtv takes the ads, screen tracking and update nags out of your Fire TV over ADB. No root, none of your apps touched, every step can be undone. Free and open source.
Evidence-first vendor and AI tool risk review app. Python, Flask, explainable rules, live demo.
SecurityPython · HTMLGitHub @doneal78OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗A small Flask application for evidence-first vendor and AI-tool risk assessments. It is a portfolio project that uses synthetic data only. It is not a production system, and it has no live ServiceNow integration.
The local Flask application has an app factory, a health-check endpoint, a responsive demo interface, and pytest coverage.
The risk engine in app/services/risk_engine.py evaluates assessments against the versioned illustrative rules in app/data/risk_rules.json and the single org policy in app/data/org_policy.json. It is a draft portfolio model, not a calibrated methodology, and the policy profile is illustrative rather than an approved corporate policy.
Intake asks whether the AI makes fully automated decisions about people's eligibility, safety, or similar outcomes with no human review. A yes answer produces a prohibited outcome that cites restriction HR-001 and policy ORG-AI-USE version 1.1.0. That outcome is separate from the score. The required contract clause in the starter profile, no vendor model training on company or customer data, is checked against vendor evidence. Verified, self-attested, missing, conflicting, expired, and not reviewed use the same evidence vocabulary as a claim. A gap is shown to the reviewer and does not block the decision. Another restriction or clause is added by extending those lists. On a saved risk, a person records approve, approve with conditions, escalate, or reject. Approving a prohibited result requires an exception rationale. Escalating or rejecting it does not. The prohibited flag stays on the assessment. The engine does not make that decision. A low residual tier is not an approval.
The public form follows intake questionnaire 1.1.0. Model training is asked only when AI use is not none. A trust-center URL can be stored with pasted SOC, privacy, and security choices. Those choices become evidence records. The application does not fetch the URL, and the URL does not approve the vendor. Optional vendor-direction and residual-direction answers, plus a short note, are context for a person. They are not scored, and they do not change the residual tier. A reviewer can download one vendor as JSON and as a text summary from the public result, a demo scenario, or a saved risk. The download includes direction context when the intake collected it, and the decision when one is recorded. It is a handoff, not a disposition.
/try/import reads a questionnaire CSV or .xlsx file, or a simulated ServiceNow JSON or CSV ticket file, then shows a preview before running the same intake path. The ServiceNow file is not a live connector. Preview and confirm do not save. The result page can save one synthetic vendor when a person checks the box and records a disposition and rationale. See docs/import-formats.md. Reading .xlsx uses openpyxl. CSV and JSON stay in the Python standard library.
A script can run that same intake without the browser. python -m app.cli assess request.json and POST /api/v1/intake/assess return the risk engine's residual tier, explanations, escalations, and rule-set version. Nothing is written unless the caller sets persist to true and supplies a human disposition, rationale, and decider. See docs/bot-contract.md.
Phase 3 is implemented as framework-independent services. The evidence validator in app/services/evidence_validator.py derives claim evidence status using configurable freshness limits in app/data/evidence_policy.json. The control mapper in app/services/control_mapper.py joins explicitly tagged claims to mapping set 0.2.0 in app/data/control_mappings.json. That set is a small NIST SP 800-53 library plus four draft ISO/IEC 27001:2022 Annex A controls and four draft ISO/IEC 42001:2023 Annex A controls. The ISO entries are draft portfolio review prompts. Control mappings lists the whole library. Assessment pages show a prompt only when a claim topic matches. See docs/evidence-model.md and docs/control-mapping-methodology.md.
A guided walkthrough at /walkthrough leads through those same pages: intake, the four results kept apart, the contradicted contract clause, the conflicting permissions claim, the high-risk AI case, the prohibited eligibility case, an opt-in save, a human decision and reviewer-package export, a file import, then the dashboard and methods. A cookie remembers the last stop in that browser for 12 hours. The guide does not score or save on its own. Pages without ?walk= stay free-form.
The local web interface includes repeatable synthetic scenarios that run the same risk, evidence, policy, and mapping services and show explanations and trace links. One scenario is an eligibility tool whose score stays moderate while the policy outcome is prohibited. A scenario can be added to the local SQLite risk register with a follow-up action, a human decision, and workflow history. The dashboard counts that register: saved assessments, open remediation, residual tiers, evidence statuses, dispositions, elapsed intake-to-disposition time when both workflow events exist, self-reported handling minutes when a decision includes them, self-reported rework minutes when a decision includes them, self-reported manual touches when a decision includes them, and self-reported handoffs when a decision includes them. A separate panel keeps the reproducible modeled labor-capacity example. The modeled panel is an illustrative formula. Elapsed cycle time is a clock span. Handling minutes, rework minutes, manual touches, and handoffs are optional, self-reported, and separate from each other. None of those figures is copied into the formula. Authenticated effort is a separate optional minute count from a signed-in reviewer. Cash savings are a separate optional dollar count from that same reviewer. Neither is copied into the formula. Avoided-incident value is a separate modeled-assumption card. The shipped assumptions leave both inputs null, so that card says Not measured. See docs/BUILD_PLAN.md and docs/cost-model.md.
Windows with Git Bash and Git, plus Python 3.12 or newer. Development uses Python 3.14; the hosted server runs Ubuntu 24.04's Python 3.12, and the test suite runs on both.
cd ~/Documents/grc-assurance-engine
python -m venv .venv
source .venv/Scripts/activate
python -m pip install -r requirements.txt
python run.py
Then open http://127.0.0.1:5000/ in a browser. The app listens on 127.0.0.1 only, with debug mode off. Stop it with Ctrl+C. Do not expose the development server to the internet.
Open Walkthrough in the header, or go to /walkthrough, and choose Start at intake. The guide is thirteen stops on the live pages. Back moves to the previous stop. On the intake, custom-vendor, and trust-center stops, Next stays off until Run assessment on that stop succeeds. File import keeps Next off until confirm shows the assessment. The decision stop keeps Next off until that saved risk has a recorded decision. The save stop keeps Next off until the case is in the register. The scenario pages, the dashboard, and methods offer Next right away, because those pages are the step. A cookie in this browser remembers the last stop for 12 hours. Start over on the guide page clears that cookie and does not delete the risk register. The pages do the scoring. The notes only say what to notice. An address without ?walk= is the free-form page. Ready-made cards on a stop that is still waiting keep that stop's ?walk= value, so opening one does not advance Next.
/try/. The ready-made vendors are questionnaires. Running one does not save it.?walk= stop on the form stays on that stop.Control mappings is linked from the assessment pages. Saved rows stay in the ignored SQLite database under instance/.
The local demo does not require credentials, network services, or private evidence. The hosted demo is open with no login. The application still has an optional HTTP Basic gate, and it stays off unless REQUIRE_AUTH is set to true. The hosted site does not set that variable. See docs/deployment.md.
python -m pytest
GitHub Actions runs this suite on Python 3.12 and 3.14 for every push and pull request.
python -m app.cli assess request.json
python -m app.cli preview vendor-intake.csv --as-of 2026-10-14
python -m app.cli assess-file servicenow-intake.json --as-of 2026-10-14
python -m app.cli assess request.json --persist --disposition escalate --rationale "Low tier is not an approval." --decider "GRC analyst"
assess reads a JSON request from a file or from stdin (-). The request form uses the same fields as the public intake form. preview parses a CSV, .xlsx, or simulated ServiceNow file and does not score it. assess-file scores every row only when the whole file is valid. --persist saves only when a disposition, rationale, and decider are present. --handling-minutes is optional. Omit it to leave handling time unmeasured. --rework-minutes is optional and separate. Omit it to leave rework time unmeasured. --manual-touches is optional and separate. Omit it to leave the touchpoint count unmeasured. --handoffs is optional and separate. Omit it to leave the handoff count unmeasured. The CLI has no signed-in session, so it has no flag for authenticated effort or cash savings and it does not store either number. The same flags on assess-file apply that one disposition to every row, or save none.
Exit 0 means an assessment was produced, or a preview can be assessed. Exit 2 means validation failed. Exit 1 means the engine raised an unexpected error. The JSON document is written to stdout.
GET /api/v1/intake/contract lists the live fields. POST /api/v1/intake/assess with Content-Type: application/json runs one request. With REQUIRE_AUTH left false, which is the demo default, the routes do not ask for a password. The request is not saved unless persist is true and decision names a disposition, rationale, and decider. Assess alone does not write. The contract, examples, and limitations are in docs/bot-contract.md.
GET /health returns status 200 and {"status": "ok", "service": "grc-assurance-engine", "database": "reachable"} when the process is running and the configured SQLite file can be opened read-only so its catalog can be read. If that read fails, the same route returns status 503 and {"status": "unavailable", "service": "grc-assurance-engine", "database": "unreachable"}. The body does not include the filesystem path, the SQL, or the driver message. The check does not validate schema completeness, integrity, or backups, and it does not show that the host is production-ready.
The health endpoint lives in app/routes/health.py, a file not listed in the original build plan. It is kept separate from the dashboard because it is an operational endpoint, not a page.
Runtime data belongs in the instance/ folder, which Git ignores. The app initializes a local SQLite database for saved synthetic risks, remediation actions, human decisions, and workflow events. Scenario fixtures and saved data are synthetic; delete the ignored local database manually if you want to reset your demo records. A database created before the policy columns existed gains those columns on startup. Rows saved before the policy check have no policy reference, and a decision cannot be recorded on them until the scenario is saved again. A decisions table created before escalate existed is rebuilt on startup. The rows already stored are kept. A decisions table created before handling_minutes existed gains that column on startup. Null means the minutes were not reported. A decisions table created before rework_minutes existed gains that column on startup. Null means rework time was not reported. A decisions table created before manual_touches existed gains that column on startup. Null means a touchpoint count was not reported. A decisions table created before handoffs existed gains that column on startup. Null means a handoff count was not reported. A decisions table created before authenticated_effort_minutes existed gains that column on startup. Null means a signed-in reviewer did not report minutes for that disposition. A decisions table created before cash_savings_dollars existed gains that column on startup. Null means a signed-in reviewer did not report a spending reduction for that disposition. A workflow-event table created before the hash chain existed gains prev_hash, hash, and chain_origin on startup. Rows already stored are hashed once and marked chained after the fact. Events written after that are hashed when they are inserted. A later startup does not rehash an edited row. A risk table created before browser sandboxes existed gains sandbox_id on startup. Those rows stay visible to every visitor. New browser saves use a signed cookie that is not a login.
Every provided record is synthetic. The org policy, the intake questionnaire, and the risk rules are illustrative. The JSON API and CLI are a portfolio contract for that same illustrative model. A response does not approve a vendor. A human disposition is stored only when the caller confirms a save, and it is a separate field from the residual tier. A trust-center URL in the demo is stored as a citation unless the form or the bot request sets fetch to yes. That GET stores vendor-published material for review. It is not verified evidence, and a pasted citation is not a live assurance feed. File import does not fetch. A ServiceNow import file is a simulated ticket export. The application does not connect to ServiceNow, and it does not open attachment bytes. A prohibited outcome is not a legal determination, and an exception rationale does not clear it. A contract-clause status is an evidence result, not a legal conclusion that the clause is in force. The dashboard shows observed counts, elapsed cycle time, optional self-reported handling minutes, optional self-reported rework minutes, optional self-reported manual touches, and optional self-reported handoffs apart from the cost-of-assurance panel. Observed counts and elapsed cycle time come from saved risks and workflow events. Handling minutes, rework minutes, manual touches, and handoffs are whatever a person typed on a disposition. Blank means unmeasured. Rework minutes are not handling minutes. Manual touches are not handling minutes and not rework minutes. Handoffs are not handling minutes, not rework minutes, and not manual touches. The cost-of-assurance panel stays a modeled example from labeled assumptions. Elapsed cycle time is a clock span between events. The typed numbers are not that span, they are not checked against an identity system, and none of these figures is cash savings. The monthly assessment rate is a count of current decisions when their timestamps cover 30 days, and Not measured when they do not. Authenticated effort is optional minutes a signed-in reviewer entered, shown as its own median, and Not measured when no decision stores them. Cash savings are optional whole dollars a signed-in reviewer entered, shown as their own median, and Not measured when no decision stores them. They are not the modeled labor-capacity dollars, and they are not a wage times the other observed figures. Avoided-incident value is a modeled assumption on its own card. It says Not measured while both versioned inputs are null. It is not those observed figures and it is not cash savings. A decision timestamp records when the decision was stored. The workflow event log is tamper-evident, not tamper-proof: each event has a SHA-256 hash linked to the previous event, and rows that already existed are marked chained after the fact. Someone who can rewrite the database can rebuild the chain, so it is not immutable storage, a production audit log, or a certification. The decider's name is not authenticated. What production use would still require is listed in docs/architecture.md. The ISO/IEC 27001 and ISO/IEC 42001 entries in mapping set 0.2.0 are draft portfolio review prompts. Mapping a requirement to a control does not prove implementation or compliance, and no part of this project claims conformity, certification, cross-framework equivalence, production readiness, or proven risk reduction. The hosted demo is open with no login and uses synthetic data only; it is not production-ready. A confirmed import save and a persist request write into that browser's sandbox. The sandbox cookie is not a login. Anyone holding it can read that sandbox. Clearing cookies drops the link. Seeded scenario pages stay visible to every visitor.
The hosted demo is open at https://grc-assurance.davidoneal.dev, with no login, when the host is up. Start at https://grc-assurance.davidoneal.dev/try/ to run a vendor review without changing anything. The guided path is /walkthrough. The host serves the commit last installed with infra/deploy-app-revision.sh. A push to main does not deploy. The Flask application runs on an AWS Lightsail Ubuntu instance provisioned by CloudFormation, with Nginx and Gunicorn serving it and SQLite storing synthetic saved risks on the instance. Cloudflare proxies the hostname and connects to the origin over HTTPS in Full (strict) mode. Daily Lightsail snapshots provide recovery points. One restore drill, on 2026-10-27, is recorded in the deployment note. A later snapshot is untested until another drill is recorded. GitHub Actions can run the install script from a manual workflow dispatch after repository secrets are set. See docs/deployment.md for the command, the secrets, what was recorded, and what this repository does not re-check.
The host is a small portfolio demo, not a production service. Its Lightsail bundle is $7/month before snapshot storage or other charges. SQLite is suitable for this single-instance synthetic demo only. All data must remain synthetic. There is no login. A browser's opt-in saves stay in that browser's sandbox cookie, which is not an account. Seeded scenario pages stay visible to every visitor. Rows with no sandbox id stay visible to every visitor. The repository Nginx file limits /try/ and /api/ to 20 requests a minute per visitor and other pages to 120. The live host uses the file installed on the server.
Open /walkthrough. On the intake form stops, run the assessment, then use Next. The note on each page is the cue: a made-up vendor on the public form, an optional trust-center URL whose fetch stores an excerpt for review and is not verified evidence, four results kept apart, CC-001 verified then conflicting, a permissions claim that does escalate, a review a score cannot skip, a prohibited outcome beside a low residual tier, an opt-in save, a decision whose export includes that decision, a file import whose preview has no score, then the dashboard and methods. The cookie remembers the stop for 12 hours. The guide does not change a page unless the address includes ?walk=.
The notes below are the same story, plus a few edges the stops do not require you to submit: contradicting evidence, an unknown answer, and the model-training question appearing only when AI use is not none.
/try/ and run the evidence-supported vendor. Point out that inherent risk, evidence confidence, residual risk, and the policy outcome are shown separately. CC-001 is verified from the reviewed contract excerpt. Run Contract clause contradicted and show that CC-001 is conflicting. That status does not change the inherent score, the residual tier, or the review path. The two vendors do not share a score, because their other answers differ./try/import, preview the sample ServiceNow JSON. Point out that ticket priority is context and the residual tier appears only after confirm, from the risk engine. Confirm does not save the vendor. Saving requires the checkbox, a disposition, and a rationale. The register then shows the disposition beside the engine tier.The guided scenario runner, event log, and mock ticket presentation were inspired, with permission, by irvinthev's GRC Cost of Assurance Dashboard. The code, rules, and data in this repository are separate and original.
/try/import. Next stays off through preview. Preview a sample. The preview has no score. Ticket priority does not set residual risk. Confirm runs the same engine and does not save, and then Next continues. Saving is a checkbox, and the disposition is separate from the residual tier. ServiceNow is not called. Opening the upload form again holds Next, because the cookie stores the stop and not the confirmation./try/ run that is not saved is in neither number. Cycle time stays empty for a scenario save, because that path does not store both an intake event and a disposition event. Self-reported handling minutes appear only when a decision includes them. Self-reported rework minutes are a separate card and appear only when a decision includes them. Self-reported manual touches are a separate card and appear only when a decision includes them. Self-reported handoffs are a separate card and appear only when a decision includes them. The monthly assessment rate is a separate card. It counts current decisions in the 30 days ending at the latest decision timestamp, and only when those timestamps cover at least 30 days. A shorter history says Not measured. Authenticated effort is a separate card. It shows the median of optional minutes a signed-in reviewer entered, and it says Not measured when no decision stores them. The public try form does not ask for it. Cash savings are a separate card. They show the median of optional whole dollars a signed-in reviewer entered, and the card says Not measured when no decision stores them. The public try form does not ask for them. The cost panel is modeled labor capacity, not those reported dollars, and its assessments-per-month figure is an assumption, not that rate, not authenticated effort, and not cash savings. Avoided-incident value is a separate card. It is a modeled assumption, and it says Not measured while both versioned inputs are null.incident_cost and avoided_incident_count are null, and its own card says Not measured. Mappings stay draft. The page does not certify a vendor.