@apideck/quickbooks-review-check 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +239 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,67 +1,276 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Apideck QuickBooks Review Check
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@apideck/quickbooks-review-check)
|
|
4
|
+
[](LICENSE)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
**Free, open-source self-check for the Intuit QuickBooks Online app security review.** Run it before you submit, and on every deploy after you're listed, so Intuit's yearly re-review doesn't catch you out.
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|---|---|---|
|
|
9
|
-
| **Runtime** | HTTPS enforcement, TLS 1.0/1.1/1.2/1.3, certificate, HSTS, `Cache-Control: no-store` (not `private`) on sensitive pages, TRACE/TRACK disabled, `Secure` + `HttpOnly` session cookies, the **302 rule** on your OAuth callback, open redirects, version-banner disclosure, browser security headers | Probes your deployed app from outside |
|
|
10
|
-
| **Source** | Refresh token **and realmId** encrypted at rest (AES/3DES), AES key not inline, tokens or `authResponse` logged, tokens sent to the browser / `localStorage`, hardcoded `state: 'testState'` (CSRF in Intuit's sample code), missing state validation, CSRF middleware, TLS verification disabled, weak ciphers (ECB, `createCipher`), SQL/QBO-query string interpolation, XSS sinks, XXE, QuickBooks data flowing into LLM/analytics/APM SDKs | Static scan of your repo (JS/TS, Python, Ruby, PHP, Java, C#, Go). No code leaves your machine. |
|
|
8
|
+
It applies to apps for **QuickBooks Online and Intuit Enterprise Suite**: Intuit's IES developer docs say IES apps follow the identical publishing steps and three-part review.
|
|
11
9
|
|
|
12
|
-
|
|
10
|
+
Try it in the browser at **[apideck.com/tools/quickbooks-review](https://www.apideck.com/tools/quickbooks-review)**, run it locally with `npx`, or add it to CI as a GitHub Action.
|
|
13
11
|
|
|
14
12
|
> **Disclaimer.** This is a self-assessment aid, not a certification, penetration test, security audit or legal/compliance advice. Passing these checks does not mean an app meets Intuit's requirements or will be approved. Always confirm requirements against Intuit's official developer documentation. Only run runtime checks against apps you own or are authorized to test. Provided "as is" under the MIT License, without warranty.
|
|
15
13
|
>
|
|
16
14
|
> Not affiliated with, endorsed by or sponsored by Intuit Inc. Intuit and QuickBooks are trademarks of Intuit Inc., used only to describe what the tool checks against.
|
|
17
15
|
|
|
16
|
+
## Contents
|
|
17
|
+
|
|
18
|
+
- [What it checks](#what-it-checks)
|
|
19
|
+
- [Quick start](#quick-start)
|
|
20
|
+
- [GitHub Action guide](#github-action-guide)
|
|
21
|
+
- [Reading the results](#reading-the-results)
|
|
22
|
+
- [CLI reference](#cli-reference)
|
|
23
|
+
- [Troubleshooting](#troubleshooting)
|
|
24
|
+
- [Fixing common findings](#fixing-common-findings)
|
|
25
|
+
- [Using Apideck?](#using-apideck)
|
|
26
|
+
- [Sample apps](#sample-apps)
|
|
27
|
+
- [Development](#development)
|
|
28
|
+
|
|
29
|
+
## What it checks
|
|
30
|
+
|
|
31
|
+
| | What it checks | How |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| **Runtime** | HTTPS enforcement, TLS 1.0/1.1/1.2/1.3, certificate, HSTS, `Cache-Control: no-store` (not `private`) on sensitive pages, TRACE/TRACK disabled, `Secure` + `HttpOnly` session cookies, the **302 rule** on your OAuth callback, open redirects, version-banner disclosure, browser security headers | Ordinary HTTP requests and TLS handshakes against your deployed app |
|
|
34
|
+
| **Source** | Refresh token **and realmId** encrypted at rest (AES/3DES), encryption key not inline, tokens or `authResponse` logged, tokens sent to the browser or `localStorage`, a hardcoded `state: 'testState'` (CSRF, from Intuit's sample code), missing state validation, CSRF middleware, TLS verification disabled, weak ciphers (ECB, `createCipher`), SQL and QuickBooks-query string interpolation, XSS sinks, XXE, QuickBooks data near LLM/analytics/APM SDKs | Pattern-based scan of your repo (JS/TS, Python, Ruby, PHP, Java, C#, Go). Nothing is uploaded. |
|
|
35
|
+
|
|
36
|
+
Every result maps to one of Intuit's review areas. Each report ends with a **sign-off checklist** for what no scanner can prove: patching, access control, a penetration test or DAST scan, and the QuickBooks data-usage rules.
|
|
37
|
+
|
|
18
38
|
## Quick start
|
|
19
39
|
|
|
40
|
+
**In the browser:** [apideck.com/tools/quickbooks-review](https://www.apideck.com/tools/quickbooks-review). Probe a deployed app, or pick a folder to scan in the browser, and download an auditor-style PDF report.
|
|
41
|
+
|
|
42
|
+
**From your terminal** (Node 20 or later, nothing to install):
|
|
43
|
+
|
|
20
44
|
```bash
|
|
21
45
|
npx @apideck/quickbooks-review-check \
|
|
22
46
|
--url https://app.example.com \
|
|
23
47
|
--callback /oauth/quickbooks/callback \
|
|
24
|
-
--path /dashboard
|
|
48
|
+
--path /dashboard \
|
|
25
49
|
--header "Cookie: session=…" \
|
|
26
50
|
--source .
|
|
27
51
|
```
|
|
28
52
|
|
|
29
|
-
|
|
30
|
-
- `--path` names sensitive pages that must send `no-store`. Pass a session cookie with `--header` so they're checked while signed in, not as a redirect to the login page.
|
|
31
|
-
- `--markdown report.md` and `--json report.json` write reports. `--fail-on warn|fail|never` controls the exit code.
|
|
32
|
-
|
|
33
|
-
## GitHub Action
|
|
53
|
+
**In CI:** add `.github/workflows/quickbooks-review.yml`:
|
|
34
54
|
|
|
35
55
|
```yaml
|
|
36
56
|
name: QuickBooks review check
|
|
37
57
|
on:
|
|
38
|
-
deployment_status:
|
|
39
|
-
schedule: [{ cron: '0 6 * * 1' }] # weekly, catches drift before Intuit's annual re-review
|
|
40
58
|
pull_request:
|
|
59
|
+
schedule:
|
|
60
|
+
- cron: '0 6 * * 1' # every Monday
|
|
61
|
+
|
|
62
|
+
jobs:
|
|
63
|
+
check:
|
|
64
|
+
runs-on: ubuntu-latest
|
|
65
|
+
steps:
|
|
66
|
+
- uses: actions/checkout@v4
|
|
67
|
+
- uses: apideck-libraries/quickbooks-review-check@v0
|
|
68
|
+
with:
|
|
69
|
+
url: https://app.example.com
|
|
70
|
+
callback: /oauth/quickbooks/callback
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The report appears in the workflow run's job summary.
|
|
74
|
+
|
|
75
|
+
## GitHub Action guide
|
|
76
|
+
|
|
77
|
+
### Inputs
|
|
78
|
+
|
|
79
|
+
| Input | Default | Description |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `url` | | Base URL of the deployed app. Runs the runtime checks. Leave empty to only scan source. |
|
|
82
|
+
| `callback` | | Your OAuth redirect URI path, as registered with Intuit (e.g. `/oauth/quickbooks/callback`). Checks the 302 rule, caching and cookies on it. |
|
|
83
|
+
| `paths` | | Sensitive pages that must not be cached, one per line (e.g. `/dashboard`). |
|
|
84
|
+
| `headers` | | Extra request headers, one per line. Use it to send a session cookie so signed-in pages are checked. Always pass values from secrets. |
|
|
85
|
+
| `source` | `.` | Directory to scan. Set to `''` to skip the source scan. |
|
|
86
|
+
| `fail-on` | `fail` | When the step fails: `fail` (any failed check), `warn` (also on warnings) or `never`. |
|
|
87
|
+
|
|
88
|
+
### Outputs
|
|
89
|
+
|
|
90
|
+
| Output | Description |
|
|
91
|
+
|---|---|
|
|
92
|
+
| `report` | Path to the Markdown report |
|
|
93
|
+
| `report-json` | Path to the JSON report |
|
|
94
|
+
| `failed` | Number of failed checks |
|
|
95
|
+
| `warnings` | Number of warnings |
|
|
96
|
+
| `passed` | Number of passed checks |
|
|
97
|
+
|
|
98
|
+
The outputs are set even when the step fails, so later steps with `if: always()` can use them.
|
|
99
|
+
|
|
100
|
+
### Recipe: check every deployment
|
|
101
|
+
|
|
102
|
+
Run after each successful deploy (Vercel, Netlify, Render and others create GitHub deployments) against the URL that was just deployed:
|
|
103
|
+
|
|
104
|
+
```yaml
|
|
105
|
+
on: deployment_status
|
|
106
|
+
|
|
107
|
+
jobs:
|
|
108
|
+
check:
|
|
109
|
+
if: github.event.deployment_status.state == 'success'
|
|
110
|
+
runs-on: ubuntu-latest
|
|
111
|
+
steps:
|
|
112
|
+
- uses: actions/checkout@v4
|
|
113
|
+
with:
|
|
114
|
+
ref: ${{ github.event.deployment.sha }}
|
|
115
|
+
- uses: apideck-libraries/quickbooks-review-check@v0
|
|
116
|
+
with:
|
|
117
|
+
url: ${{ github.event.deployment_status.environment_url }}
|
|
118
|
+
callback: /oauth/quickbooks/callback
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
If preview deployments sit behind a login (for example Vercel Deployment Protection), point `url` at production instead, or pass the provider's bypass header through `headers`.
|
|
122
|
+
|
|
123
|
+
### Recipe: weekly drift check
|
|
124
|
+
|
|
125
|
+
Listed apps are reviewed again by Intuit. A weekly run catches a changed header or proxy setting long before that:
|
|
126
|
+
|
|
127
|
+
```yaml
|
|
128
|
+
on:
|
|
129
|
+
schedule:
|
|
130
|
+
- cron: '0 6 * * 1'
|
|
131
|
+
workflow_dispatch:
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Recipe: source-only check on pull requests
|
|
135
|
+
|
|
136
|
+
No deployed URL needed. Catches a new `console.log(token)` or a hardcoded key before merge:
|
|
137
|
+
|
|
138
|
+
```yaml
|
|
139
|
+
on: pull_request
|
|
41
140
|
|
|
42
141
|
jobs:
|
|
43
142
|
check:
|
|
44
143
|
runs-on: ubuntu-latest
|
|
45
144
|
steps:
|
|
46
145
|
- uses: actions/checkout@v4
|
|
146
|
+
- uses: apideck-libraries/quickbooks-review-check@v0
|
|
147
|
+
with:
|
|
148
|
+
source: .
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Recipe: check signed-in pages
|
|
152
|
+
|
|
153
|
+
Without a session, sensitive pages usually redirect to your login page, and the cache check can't see them. Create a dedicated test account, copy its session cookie into a repository secret (Settings → Secrets and variables → Actions → `QBRC_SESSION_COOKIE`), then:
|
|
154
|
+
|
|
155
|
+
```yaml
|
|
47
156
|
- uses: apideck-libraries/quickbooks-review-check@v0
|
|
48
157
|
with:
|
|
49
158
|
url: https://app.example.com
|
|
50
159
|
callback: /oauth/quickbooks/callback
|
|
51
160
|
paths: |
|
|
52
161
|
/dashboard
|
|
53
|
-
/settings/
|
|
162
|
+
/settings/integrations
|
|
54
163
|
headers: |
|
|
55
164
|
Cookie: ${{ secrets.QBRC_SESSION_COOKIE }}
|
|
56
|
-
source: .
|
|
57
|
-
fail-on: fail
|
|
58
165
|
```
|
|
59
166
|
|
|
60
|
-
|
|
167
|
+
Use a test account with no real customer data, and rotate the cookie when it expires.
|
|
168
|
+
|
|
169
|
+
### Recipe: keep the report as an artifact
|
|
170
|
+
|
|
171
|
+
```yaml
|
|
172
|
+
- id: qbo
|
|
173
|
+
uses: apideck-libraries/quickbooks-review-check@v0
|
|
174
|
+
with:
|
|
175
|
+
url: https://app.example.com
|
|
176
|
+
- if: always()
|
|
177
|
+
uses: actions/upload-artifact@v4
|
|
178
|
+
with:
|
|
179
|
+
name: quickbooks-review
|
|
180
|
+
path: |
|
|
181
|
+
${{ steps.qbo.outputs.report }}
|
|
182
|
+
${{ steps.qbo.outputs.report-json }}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Recipe: alert on failures without blocking
|
|
186
|
+
|
|
187
|
+
```yaml
|
|
188
|
+
- id: qbo
|
|
189
|
+
uses: apideck-libraries/quickbooks-review-check@v0
|
|
190
|
+
with:
|
|
191
|
+
url: https://app.example.com
|
|
192
|
+
fail-on: never
|
|
193
|
+
- if: steps.qbo.outputs.failed != '0'
|
|
194
|
+
run: |
|
|
195
|
+
curl -X POST -H 'Content-type: application/json' \
|
|
196
|
+
--data "{\"text\":\"QuickBooks review check: ${{ steps.qbo.outputs.failed }} failed, ${{ steps.qbo.outputs.warnings }} warnings\"}" \
|
|
197
|
+
"${{ secrets.SLACK_WEBHOOK_URL }}"
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Reading the results
|
|
201
|
+
|
|
202
|
+
Each check has one of five statuses:
|
|
203
|
+
|
|
204
|
+
| Status | Meaning |
|
|
205
|
+
|---|---|
|
|
206
|
+
| **Fail** | The control is missing or wrong. Expect Intuit to flag it. |
|
|
207
|
+
| **Warn** | Partly in place, or worth a look (source patterns can be false positives). |
|
|
208
|
+
| **Pass** | The control is in place. |
|
|
209
|
+
| **Info** | Context, such as the HTTP methods your server advertises. |
|
|
210
|
+
| **Skip** | Not checked, for example no cookies were seen or no callback was given. |
|
|
211
|
+
|
|
212
|
+
Results are grouped by Intuit's review areas: server and transport, attack vulnerabilities, cookies and sessions, OAuth token management, the token-endpoint redirect rule, and QuickBooks data usage. Each finding includes the evidence (the response header or `file:line`) and a fix.
|
|
213
|
+
|
|
214
|
+
The website version adds a PDF report with severities (High, Medium, Low), evidence, remediation and a sign-off page, which is useful to keep with your submission. Intuit requires critical, high and medium issues found in its review to be fixed before an app is published.
|
|
215
|
+
|
|
216
|
+
**Exit codes:** `0` all good (for the chosen `--fail-on`), `1` failures found, `2` usage error.
|
|
217
|
+
|
|
218
|
+
## CLI reference
|
|
219
|
+
|
|
220
|
+
```text
|
|
221
|
+
npx @apideck/quickbooks-review-check [options]
|
|
222
|
+
|
|
223
|
+
Runtime checks (probe your deployed app):
|
|
224
|
+
--url <url> Base URL of your deployed app
|
|
225
|
+
--callback <path> OAuth redirect URI path (checks the 302 rule, cache headers, cookies)
|
|
226
|
+
--path <path> Sensitive page to check for no-store caching (repeatable)
|
|
227
|
+
--header "K: V" Extra request header, e.g. a session cookie (repeatable)
|
|
228
|
+
--timeout <ms> Per-request timeout (default 10000)
|
|
229
|
+
--http-port <port> Port for the plain-HTTP redirect check (default 80; for local testing)
|
|
230
|
+
|
|
231
|
+
Source checks (static scan of your code):
|
|
232
|
+
--source <dir> Scan a repository for token-handling issues
|
|
233
|
+
--include-tests Also scan test/example files
|
|
234
|
+
|
|
235
|
+
Output:
|
|
236
|
+
--markdown <file> Write a Markdown report ("-" for stdout)
|
|
237
|
+
--json <file> Write a JSON report ("-" for stdout)
|
|
238
|
+
--fail-on <level> Exit 1 on: fail (default) | warn | never
|
|
239
|
+
-q, --quiet Don't print the console report
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Examples:
|
|
61
243
|
|
|
62
|
-
|
|
244
|
+
```bash
|
|
245
|
+
# Source only, Markdown report to a file
|
|
246
|
+
npx @apideck/quickbooks-review-check --source . --markdown quickbooks-review.md
|
|
247
|
+
|
|
248
|
+
# Runtime only, JSON to stdout for scripting
|
|
249
|
+
npx @apideck/quickbooks-review-check --url https://app.example.com --json - --quiet | jq '.summary'
|
|
63
250
|
|
|
64
|
-
|
|
251
|
+
# Fail the build on warnings too
|
|
252
|
+
npx @apideck/quickbooks-review-check --source . --fail-on warn
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Troubleshooting
|
|
256
|
+
|
|
257
|
+
**"Callback /oauth/callback returned 404".** The `callback` path must match the Redirect URI registered in the Intuit developer portal, exactly.
|
|
258
|
+
|
|
259
|
+
**"Cache-Control not checked on /dashboard".** The page redirected, usually to your login. Pass a session cookie with `headers` / `--header`.
|
|
260
|
+
|
|
261
|
+
**The callback answers 4xx with JSON.** That passes. It means your app rejected the fake test code without rendering a page. A real authorization should still end in a 302, which you can confirm with a real connect flow.
|
|
262
|
+
|
|
263
|
+
**"TLS 1.0 not testable".** Your runner's OpenSSL can't offer TLS 1.0. The other TLS results are still valid.
|
|
264
|
+
|
|
265
|
+
**TRACE shows as refused.** A firewall or CDN blocking TRACE counts as disabled, which is what Intuit wants.
|
|
266
|
+
|
|
267
|
+
**The source scan flags something that's fine.** It matches patterns, so for example an `innerHTML` that only renders static text shows as a warning. Treat warnings as prompts to look.
|
|
268
|
+
|
|
269
|
+
**Internal or private URLs.** The hosted scanner on apideck.com only checks public hosts on ports 80/443. Run the CLI or the Action inside your network for anything else.
|
|
270
|
+
|
|
271
|
+
## Fixing common findings
|
|
272
|
+
|
|
273
|
+
The **[requirements guide](https://www.apideck.com/tools/quickbooks-review/checklist)** explains every check, with a command to verify it and fixes for Express, Next.js, nginx, Django and Rails. The fix Intuit reviewers ask about most is encrypting the refresh token and realmId with AES (preferred) or 3DES, with the key kept out of your code. In Node:
|
|
65
274
|
|
|
66
275
|
```js
|
|
67
276
|
import crypto from 'node:crypto';
|
|
@@ -84,7 +293,7 @@ export function decrypt(blob) {
|
|
|
84
293
|
|
|
85
294
|
## Using Apideck?
|
|
86
295
|
|
|
87
|
-
If your app connects to QuickBooks through [Apideck
|
|
296
|
+
If your app connects to QuickBooks through [Apideck](https://www.apideck.com/accounting-api), you still register your own Intuit app and go through Intuit's review, but the OAuth redirect URI, token exchange, token storage and refresh run on Apideck Vault. Your app never holds the Intuit tokens, and the scanner recognizes this setup. You still own the runtime checks (HTTPS, cookies, cache headers) and the data-usage rules for your app, and this tool covers those.
|
|
88
297
|
|
|
89
298
|
## Sample apps
|
|
90
299
|
|
|
@@ -101,11 +310,17 @@ node bin/quickbooks-review-check.js --url https://localhost:8443 --http-port 808
|
|
|
101
310
|
|
|
102
311
|
`node examples/record.js <dir>` re-records both apps' scan results and source files for the website's "Try a sample" buttons.
|
|
103
312
|
|
|
313
|
+
## Releasing
|
|
314
|
+
|
|
315
|
+
Bump `version` in `package.json`, merge, then push a matching tag (`git tag v0.2.0 && git push origin v0.2.0`). The Release workflow runs the tests, publishes to npm with provenance via trusted publishing, and moves the major tag (`v0`) the GitHub Action uses.
|
|
316
|
+
|
|
104
317
|
## Development
|
|
105
318
|
|
|
106
319
|
```bash
|
|
107
|
-
npm test # node:test, zero dependencies
|
|
320
|
+
npm test # node:test, zero dependencies; includes the end-to-end test against both sample apps
|
|
108
321
|
node bin/quickbooks-review-check.js --source test/fixtures/insecure --fail-on never
|
|
109
322
|
```
|
|
110
323
|
|
|
324
|
+
Issues and pull requests for new checks are welcome.
|
|
325
|
+
|
|
111
326
|
MIT © Apideck
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apideck/quickbooks-review-check",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Free self-check for the Intuit QuickBooks Online app security review: runtime probes + source scan, as a CLI and GitHub Action.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|