@gspothq/cli 0.0.0-stage → 0.1.1
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 +18 -2
- package/configurations/database/postgres/manifest.toml +114 -0
- package/configurations/database/postgres/rules/POSTGRES.md +78 -0
- package/configurations/framework/astro/eslint-presets.json +138 -0
- package/configurations/framework/astro/manifest.toml +95 -0
- package/configurations/framework/astro/rules/ASTRO.md +39 -0
- package/configurations/framework/expo/manifest.toml +43 -0
- package/configurations/framework/express/manifest.toml +17 -0
- package/configurations/framework/express/rules/EXPRESS.md +73 -0
- package/configurations/framework/express/semgrep/express.yml +71 -0
- package/configurations/framework/fastapi/manifest.toml +11 -0
- package/configurations/framework/fastapi/rules/FASTAPI.md +100 -0
- package/configurations/framework/fastapi/semgrep/fastapi.yml +44 -0
- package/configurations/framework/nestjs/eslint-presets.json +62 -0
- package/configurations/framework/nestjs/manifest.toml +52 -0
- package/configurations/framework/nestjs/rules/NESTJS.md +44 -0
- package/configurations/framework/nextjs/eslint-presets.json +73 -0
- package/configurations/framework/nextjs/manifest.toml +119 -0
- package/configurations/framework/nextjs/rules/NEXTJS.md +43 -0
- package/configurations/framework/react/eslint-presets.json +77 -0
- package/configurations/framework/react/manifest.toml +93 -0
- package/configurations/framework/react/rules/REACT.md +108 -0
- package/configurations/framework/react-dom/eslint-presets.json +110 -0
- package/configurations/framework/react-dom/manifest.toml +24 -0
- package/configurations/framework/react-native/manifest.toml +46 -0
- package/configurations/framework/react-native/rules/REACT-NATIVE.md +51 -0
- package/configurations/framework/site/manifest.toml +170 -0
- package/configurations/framework/site/rules/SITE.md +20 -0
- package/configurations/framework/svelte/eslint-presets.json +67 -0
- package/configurations/framework/svelte/manifest.toml +83 -0
- package/configurations/framework/svelte/rules/SVELTE.md +48 -0
- package/configurations/framework/vue/eslint-presets.json +180 -0
- package/configurations/framework/vue/manifest.toml +83 -0
- package/configurations/framework/vue/rules/VUE.md +41 -0
- package/configurations/general/commits/manifest.toml +111 -0
- package/configurations/general/commits/rules/GIT.md +31 -0
- package/configurations/general/coverage/manifest.toml +3 -0
- package/configurations/general/dependencies/manifest.toml +159 -0
- package/configurations/general/dependencies/rules/DEPENDENCIES.md +30 -0
- package/configurations/general/dependencies/syncpack.json +15 -0
- package/configurations/general/docs/manifest.toml +79 -0
- package/configurations/general/docs/rules/DOCS.md +86 -0
- package/configurations/general/duplication/manifest.toml +32 -0
- package/configurations/general/engineering/manifest.toml +8 -0
- package/configurations/general/engineering/rules/agent/PLANNING.md +42 -0
- package/configurations/general/engineering/rules/agent/SUPPRESSIONS.md +16 -0
- package/configurations/general/engineering/rules/agent/TALKING.md +3 -0
- package/configurations/general/engineering/rules/agent/TASKS.md +19 -0
- package/configurations/general/engineering/rules/agent/WORKING.md +83 -0
- package/configurations/general/engineering/rules/code/ACCESSIBILITY.md +32 -0
- package/configurations/general/engineering/rules/code/CLI.md +37 -0
- package/configurations/general/engineering/rules/code/COMMENTS.md +38 -0
- package/configurations/general/engineering/rules/code/CONFIGURATION.md +25 -0
- package/configurations/general/engineering/rules/code/ERRORS.md +49 -0
- package/configurations/general/engineering/rules/code/HTTP.md +46 -0
- package/configurations/general/engineering/rules/code/LOGGING.md +55 -0
- package/configurations/general/engineering/rules/code/TESTING.md +58 -0
- package/configurations/general/files/manifest.toml +141 -0
- package/configurations/general/files/rules/YAML.md +19 -0
- package/configurations/general/format/manifest.toml +152 -0
- package/configurations/general/gspot/manifest.toml +65 -0
- package/configurations/general/gspot/rules/GENERATED.md +10 -0
- package/configurations/general/licenses/manifest.toml +33 -0
- package/configurations/general/naming/defaults.json +423 -0
- package/configurations/general/naming/manifest.toml +35 -0
- package/configurations/general/naming/rules/NAMING.md +179 -0
- package/configurations/general/prose/manifest.toml +86 -0
- package/configurations/general/prose/rules/WRITING.md +31 -0
- package/configurations/general/prose/vale/styles/LICENSES.md +55 -0
- package/configurations/general/prose/vale/styles/gspot/ampm.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/conscious-language.yml +16 -0
- package/configurations/general/prose/vale/styles/gspot/contractions.yml +43 -0
- package/configurations/general/prose/vale/styles/gspot/currency.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/dashes.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/date-ordinals.yml +41 -0
- package/configurations/general/prose/vale/styles/gspot/dates.yml +7 -0
- package/configurations/general/prose/vale/styles/gspot/file-paths.yml +7 -0
- package/configurations/general/prose/vale/styles/gspot/future.yml +17 -0
- package/configurations/general/prose/vale/styles/gspot/hedging.yml +22 -0
- package/configurations/general/prose/vale/styles/gspot/history.yml +16 -0
- package/configurations/general/prose/vale/styles/gspot/idioms.yml +21 -0
- package/configurations/general/prose/vale/styles/gspot/interface-verbs.yml +11 -0
- package/configurations/general/prose/vale/styles/gspot/jargon.yml +11 -0
- package/configurations/general/prose/vale/styles/gspot/leading-conjunctions.yml +12 -0
- package/configurations/general/prose/vale/styles/gspot/link-text.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/locations.yml +14 -0
- package/configurations/general/prose/vale/styles/gspot/marketing.yml +26 -0
- package/configurations/general/prose/vale/styles/gspot/merge-conflict-markers.yml +13 -0
- package/configurations/general/prose/vale/styles/gspot/militaristic-language.yml +22 -0
- package/configurations/general/prose/vale/styles/gspot/modals.yml +9 -0
- package/configurations/general/prose/vale/styles/gspot/numbered-units.yml +18 -0
- package/configurations/general/prose/vale/styles/gspot/obvious-fields.yml +14 -0
- package/configurations/general/prose/vale/styles/gspot/ordinal-adverbs.yml +15 -0
- package/configurations/general/prose/vale/styles/gspot/percentage-numbers.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/placeholders.yml +7 -0
- package/configurations/general/prose/vale/styles/gspot/product-centric-writing.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/punctuated-headings.yml +11 -0
- package/configurations/general/prose/vale/styles/gspot/self-reference.yml +15 -0
- package/configurations/general/prose/vale/styles/gspot/session-identifiers.yml +11 -0
- package/configurations/general/prose/vale/styles/gspot/since.yml +6 -0
- package/configurations/general/prose/vale/styles/gspot/straight-quotes.yml +11 -0
- package/configurations/general/prose/vale/styles/gspot/suspended-hyphens.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/symbols.yml +8 -0
- package/configurations/general/prose/vale/styles/gspot/time-ranges.yml +14 -0
- package/configurations/general/prose/vale/styles/gspot/url-prepositions.yml +10 -0
- package/configurations/general/prose/vale/styles/gspot/version-range.yml +8 -0
- package/configurations/general/repository/manifest.toml +24 -0
- package/configurations/general/secrets/gitleaks.toml +2 -0
- package/configurations/general/secrets/manifest.toml +125 -0
- package/configurations/general/secrets/rules/SECRETS.md +13 -0
- package/configurations/general/security/manifest.toml +77 -0
- package/configurations/general/security/rules/SECURITY.md +55 -0
- package/configurations/general/spelling/manifest.toml +46 -0
- package/configurations/general/structure/manifest.toml +70 -0
- package/configurations/infrastructure/ansible/manifest.toml +33 -0
- package/configurations/infrastructure/docker/manifest.toml +127 -0
- package/configurations/infrastructure/docker/rules/DOCKER.md +135 -0
- package/configurations/infrastructure/docker/trivyignore +2 -0
- package/configurations/infrastructure/github-actions/manifest.toml +76 -0
- package/configurations/infrastructure/github-actions/rules/GITHUB-ACTIONS.md +43 -0
- package/configurations/infrastructure/github-actions/zizmor.yml +3 -0
- package/configurations/infrastructure/nginx/manifest.toml +51 -0
- package/configurations/infrastructure/nginx/rules/NGINX.md +38 -0
- package/configurations/infrastructure/openapi/manifest.toml +50 -0
- package/configurations/infrastructure/openapi/rules/OPENAPI.md +39 -0
- package/configurations/infrastructure/xcode/manifest.toml +131 -0
- package/configurations/infrastructure/xcode/rules/XCODE.md +34 -0
- package/configurations/language/bash/manifest.toml +261 -0
- package/configurations/language/bash/rules/BASH.md +106 -0
- package/configurations/language/bash/rules/LANGUAGE.md +213 -0
- package/configurations/language/bash/rules/NAMING.md +62 -0
- package/configurations/language/bash/rules/OPERATIONS.md +77 -0
- package/configurations/language/bash/rules/SAFETY.md +222 -0
- package/configurations/language/bash/semgrep/bash.yml +30 -0
- package/configurations/language/css/manifest.toml +98 -0
- package/configurations/language/css/rules/CSS.md +28 -0
- package/configurations/language/css/rules/NAMING.md +17 -0
- package/configurations/language/css/rules/TAILWIND.md +26 -0
- package/configurations/language/css/stylelint-rule-names.json +89 -0
- package/configurations/language/html/manifest.toml +59 -0
- package/configurations/language/html/rules/HTML.md +27 -0
- package/configurations/language/html/rules/NAMING.md +16 -0
- package/configurations/language/javascript/eslint-presets.json +997 -0
- package/configurations/language/javascript/eslint-rule-names.json +299 -0
- package/configurations/language/javascript/eslint-runtime-names.json +61 -0
- package/configurations/language/javascript/manifest.toml +393 -0
- package/configurations/language/javascript/rules/BUN.md +13 -0
- package/configurations/language/javascript/rules/COMPONENTS.md +18 -0
- package/configurations/language/javascript/rules/DENO.md +15 -0
- package/configurations/language/javascript/rules/JAVASCRIPT.md +60 -0
- package/configurations/language/javascript/rules/NAMING.md +31 -0
- package/configurations/language/javascript/rules/NODE.md +21 -0
- package/configurations/language/javascript/rules/PLAYWRIGHT.md +34 -0
- package/configurations/language/javascript/semgrep/javascript.yml +84 -0
- package/configurations/language/markdown/manifest.toml +85 -0
- package/configurations/language/markdown/markdownlint-cli2.mjs +19 -0
- package/configurations/language/python/manifest.toml +621 -0
- package/configurations/language/python/rules/DESIGN.md +136 -0
- package/configurations/language/python/rules/FLOW.md +124 -0
- package/configurations/language/python/rules/NAMING.md +53 -0
- package/configurations/language/python/rules/PACKAGING.md +49 -0
- package/configurations/language/python/rules/PYTHON.md +136 -0
- package/configurations/language/python/rules/TYPING.md +132 -0
- package/configurations/language/sql/manifest.toml +68 -0
- package/configurations/language/sql/rules/NAMING.md +90 -0
- package/configurations/language/sql/rules/SQL.md +9 -0
- package/configurations/language/swift/manifest.toml +164 -0
- package/configurations/language/swift/rules/NAMING.md +88 -0
- package/configurations/language/swift/rules/SWIFT.md +134 -0
- package/configurations/language/swift/rules/SWIFTUI.md +61 -0
- package/configurations/language/swift/rules/UIKIT.md +99 -0
- package/configurations/language/swift/semgrep/swift.yml +126 -0
- package/configurations/language/typescript/eslint-presets.json +215 -0
- package/configurations/language/typescript/manifest.toml +72 -0
- package/configurations/language/typescript/rules/NAMING.md +73 -0
- package/configurations/language/typescript/rules/TYPESCRIPT.md +79 -0
- package/configurations/library/drizzle/manifest.toml +68 -0
- package/configurations/library/drizzle/rules/DRIZZLE.md +44 -0
- package/configurations/library/react-hook-form/manifest.toml +15 -0
- package/configurations/library/react-hook-form/rules/REACT-HOOK-FORM.md +48 -0
- package/configurations/library/swift-snapshot-testing/manifest.toml +32 -0
- package/configurations/library/tanstack-query/eslint-presets.json +21 -0
- package/configurations/library/tanstack-query/manifest.toml +26 -0
- package/configurations/library/tanstack-query/rules/TANSTACK-QUERY.md +41 -0
- package/configurations/library/testing-library/eslint-presets.json +191 -0
- package/configurations/library/testing-library/manifest.toml +47 -0
- package/configurations/library/translations/manifest.toml +40 -0
- package/configurations/library/translations/rules/NEXT-INTL.md +33 -0
- package/configurations/library/translations/rules/TRANSLATIONS.md +102 -0
- package/configurations/library/trpc/manifest.toml +14 -0
- package/configurations/library/trpc/rules/TRPC.md +43 -0
- package/configurations/library/zod/manifest.toml +25 -0
- package/configurations/library/zod/rules/ZOD.md +44 -0
- package/configurations/library/zustand/manifest.toml +18 -0
- package/configurations/library/zustand/rules/ZUSTAND.md +106 -0
- package/configurations/platform/cloudflare/manifest.toml +94 -0
- package/configurations/platform/cloudflare/rules/CLOUDFLARE.md +23 -0
- package/configurations/platform/cloudflare/semgrep/cloudflare.yml +12 -0
- package/configurations/platform/supabase/manifest.toml +112 -0
- package/configurations/platform/supabase/rules/SUPABASE.md +129 -0
- package/configurations/platform/supabase/semgrep/supabase.yml +60 -0
- package/configurations/testing/jest/manifest.toml +54 -0
- package/configurations/testing/pytest/manifest.toml +53 -0
- package/configurations/testing/swift-tests/manifest.toml +52 -0
- package/configurations/testing/swift-tests/rules/SWIFT-TESTS.md +79 -0
- package/configurations/testing/vitest/eslint-presets.json +22 -0
- package/configurations/testing/vitest/manifest.toml +69 -0
- package/configurations/testing/vitest/rules/VITEST.md +18 -0
- package/dist/LICENSE.md +201 -0
- package/dist/THIRD-PARTY-NOTICES.md +25 -0
- package/dist/eslint-rule-levels.json +10 -0
- package/dist/gspot.js +30740 -0
- package/dist/linkinator-program.js +65 -0
- package/dist/purgecss-program.js +51 -0
- package/dist/tool-versions.json +3 -0
- package/grammars/bash.wasm +0 -0
- package/grammars/css.wasm +0 -0
- package/grammars/html.wasm +0 -0
- package/grammars/javascript.wasm +0 -0
- package/grammars/licenses/tree-sitter-bash.txt +21 -0
- package/grammars/licenses/tree-sitter-css.txt +21 -0
- package/grammars/licenses/tree-sitter-html.txt +21 -0
- package/grammars/licenses/tree-sitter-javascript.txt +21 -0
- package/grammars/licenses/tree-sitter-python.txt +21 -0
- package/grammars/licenses/tree-sitter-swift.txt +21 -0
- package/grammars/licenses/tree-sitter-typescript.txt +21 -0
- package/grammars/python.wasm +0 -0
- package/grammars/swift.wasm +0 -0
- package/grammars/tsx.wasm +0 -0
- package/grammars/typescript.wasm +0 -0
- package/gspot.schema.json +45991 -0
- package/package.json +93 -5
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
- id: gspot.express.raw-query-interpolation
|
|
3
|
+
patterns:
|
|
4
|
+
- pattern: $DB.$METHOD(`...${req.$SOURCE.$FIELD}...`, ...)
|
|
5
|
+
- metavariable-regex:
|
|
6
|
+
metavariable: $METHOD
|
|
7
|
+
regex: ^(query|execute|raw|\$queryRawUnsafe)$
|
|
8
|
+
- metavariable-regex:
|
|
9
|
+
metavariable: $SOURCE
|
|
10
|
+
regex: ^(body|query|params)$
|
|
11
|
+
message: 'SQL injection risk: request data is interpolated into a database call.
|
|
12
|
+
Use parameterised queries or an ORM.'
|
|
13
|
+
languages:
|
|
14
|
+
- typescript
|
|
15
|
+
- javascript
|
|
16
|
+
severity: ERROR
|
|
17
|
+
metadata:
|
|
18
|
+
cwe:
|
|
19
|
+
- CWE-89
|
|
20
|
+
category: security
|
|
21
|
+
- id: gspot.express.unvalidated-redirect
|
|
22
|
+
patterns:
|
|
23
|
+
- pattern-either:
|
|
24
|
+
- pattern: res.redirect(req.body.$VALUE)
|
|
25
|
+
- pattern: res.redirect(req.query.$VALUE)
|
|
26
|
+
- pattern: res.redirect(req.params.$VALUE)
|
|
27
|
+
message: 'Open redirect: request data is passed directly to res.redirect().
|
|
28
|
+
Validate against an allowlist of URLs.'
|
|
29
|
+
languages:
|
|
30
|
+
- typescript
|
|
31
|
+
- javascript
|
|
32
|
+
severity: ERROR
|
|
33
|
+
metadata:
|
|
34
|
+
cwe:
|
|
35
|
+
- CWE-601
|
|
36
|
+
category: security
|
|
37
|
+
- id: gspot.express.res-send-raw-input
|
|
38
|
+
patterns:
|
|
39
|
+
- pattern-either:
|
|
40
|
+
- pattern: res.send(req.$SOURCE.$FIELD)
|
|
41
|
+
- pattern: res.send(`...${req.$SOURCE.$FIELD}...`)
|
|
42
|
+
- metavariable-regex:
|
|
43
|
+
metavariable: $SOURCE
|
|
44
|
+
regex: ^(body|query|params)$
|
|
45
|
+
message: 'Reflected XSS risk: request data is sent as text or HTML. Escape HTML
|
|
46
|
+
output or use res.json() to send JSON.'
|
|
47
|
+
languages:
|
|
48
|
+
- typescript
|
|
49
|
+
- javascript
|
|
50
|
+
severity: ERROR
|
|
51
|
+
metadata:
|
|
52
|
+
cwe:
|
|
53
|
+
- CWE-79
|
|
54
|
+
category: security
|
|
55
|
+
- id: gspot.express.no-stack-trace-in-response
|
|
56
|
+
patterns:
|
|
57
|
+
- pattern-either:
|
|
58
|
+
- pattern: 'res.send({..., stack: $ERR.stack, ...})'
|
|
59
|
+
- pattern: 'res.json({..., stack: $ERR.stack, ...})'
|
|
60
|
+
- pattern: 'res.status(...).send({..., stack: $ERR.stack, ...})'
|
|
61
|
+
- pattern: 'res.status(...).json({..., stack: $ERR.stack, ...})'
|
|
62
|
+
message: Stack traces in API responses leak internal implementation details. Log
|
|
63
|
+
the stack server-side and return a generic error message.
|
|
64
|
+
languages:
|
|
65
|
+
- typescript
|
|
66
|
+
- javascript
|
|
67
|
+
severity: ERROR
|
|
68
|
+
metadata:
|
|
69
|
+
cwe:
|
|
70
|
+
- CWE-209
|
|
71
|
+
category: security
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
[configuration]
|
|
2
|
+
title = "FastAPI"
|
|
3
|
+
requires = ["python"]
|
|
4
|
+
suggests = ["openapi"]
|
|
5
|
+
description = "Checks FastAPI route declarations with Ruff. Adds Semgrep request-handling rules when security is selected and suggests the OpenAPI configuration."
|
|
6
|
+
|
|
7
|
+
[detect]
|
|
8
|
+
dependencies = ["fastapi"]
|
|
9
|
+
|
|
10
|
+
[ruff_rules]
|
|
11
|
+
recommended = ["FAST001", "FAST002", "FAST003"]
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# FastAPI
|
|
2
|
+
|
|
3
|
+
Structure, parameters, schemas, responses, errors, and the runtime of a Python project on FastAPI
|
|
4
|
+
and Pydantic v2.
|
|
5
|
+
|
|
6
|
+
## Structure
|
|
7
|
+
|
|
8
|
+
<!-- level: all -->
|
|
9
|
+
|
|
10
|
+
Path operations adapt HTTP input to application calls and results to HTTP output; business logic
|
|
11
|
+
lives outside them. One main module creates the `FastAPI` object and wires routers, global
|
|
12
|
+
dependencies, middleware, exception handlers, and startup. Routers group related operations under
|
|
13
|
+
a prefix that does not end in `/`, and imports use their domain, `items.router` and `users.router`.
|
|
14
|
+
Database, SDK, and service clients stay out of import-time work, and nothing patches `sys.path`.
|
|
15
|
+
|
|
16
|
+
```python
|
|
17
|
+
"""Expose a fixed catalog with validated identifiers and public responses."""
|
|
18
|
+
|
|
19
|
+
from typing import Annotated
|
|
20
|
+
|
|
21
|
+
from fastapi import Path, FastAPI, APIRouter, HTTPException, status
|
|
22
|
+
from pydantic import BaseModel
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Item(BaseModel):
|
|
26
|
+
"""Public catalog item."""
|
|
27
|
+
|
|
28
|
+
name: str
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
CATALOG = (Item(name="Notebook"), Item(name="Pencil"))
|
|
32
|
+
router = APIRouter(prefix="/items", tags=["items"])
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@router.get("/{item_id}")
|
|
36
|
+
def get_item(item_id: Annotated[int, Path(ge=0)]) -> Item:
|
|
37
|
+
"""Return one catalog item or a public not-found response."""
|
|
38
|
+
if item_id >= len(CATALOG):
|
|
39
|
+
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Item not found")
|
|
40
|
+
return CATALOG[item_id]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
app = FastAPI(openapi_url=None, docs_url=None, redoc_url=None)
|
|
44
|
+
app.include_router(router)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Parameters
|
|
48
|
+
|
|
49
|
+
`Annotated[..., Query(...)]`, `Path`, `Body`, `Header`, `Cookie`, `Form`, `File`, or `Depends`;
|
|
50
|
+
a plain scalar supplies a query parameter. A parameter without a default requires a value, even when it
|
|
51
|
+
accepts `None`. Constraints such as `max_length` or `ge` belong at the boundary when the public interface requires them. Use a dependency for a check that needs a database or authorization state, not
|
|
52
|
+
a validator. Declare fixed routes such as `/users/me` before `/users/{user_id}`, and group
|
|
53
|
+
pagination or filter parameters into a query model with `extra="forbid"`.
|
|
54
|
+
|
|
55
|
+
Forms need `python-multipart`, and one operation never mixes JSON body fields with form fields
|
|
56
|
+
or files. Uploads arrive as `UploadFile`, bounded in count and size; `bytes` holds only small
|
|
57
|
+
values. Never use a filename as a filesystem path, a content type never proves safety, and never log uploaded
|
|
58
|
+
contents.
|
|
59
|
+
|
|
60
|
+
## Schemas and responses
|
|
61
|
+
|
|
62
|
+
Pydantic models define the schemas where data enters or leaves. Use separate classes for create, read, and update shapes when
|
|
63
|
+
their fields differ. Never return an input with passwords or tokens as the response. Known JSON
|
|
64
|
+
shapes use nested models with typed containers. Use `UUID` for identifiers, timezone-aware instants across a
|
|
65
|
+
boundary, and `Decimal` for money. A partial update reads
|
|
66
|
+
`model_dump(exclude_unset=True)`, merges with the stored value, and validates the complete model,
|
|
67
|
+
because `model_copy(update=...)` validates nothing.
|
|
68
|
+
|
|
69
|
+
The return type defines the public schema; `response_model` serves a returned object of another
|
|
70
|
+
shape. A dedicated output model filters fields, never `response_model_exclude`, which still
|
|
71
|
+
documents the full model. A response validation failure shows a server bug. Fix the data or
|
|
72
|
+
the schema.
|
|
73
|
+
|
|
74
|
+
## Status codes and errors
|
|
75
|
+
|
|
76
|
+
Non-default success codes go in the decorator with `fastapi.status` constants: 201 for creation,
|
|
77
|
+
202 for accepted work, and 204 for a deliberate empty body. Raise `HTTPException` rather than returning it. Its `detail` supplies a public error message. Existence and
|
|
78
|
+
authorization checks answer alike, so responses do not reveal a private resource. Global exception
|
|
79
|
+
handlers return the standard error shape and never echo a validation error's body.
|
|
80
|
+
|
|
81
|
+
## Runtime
|
|
82
|
+
|
|
83
|
+
- Declare a path operation or dependency `async def` when it awaits async libraries and `def` when it
|
|
84
|
+
calls blocking code; a blocking call inside `async def` stalls the event loop. Run CPU-heavy work in a worker process, not in `async def`.
|
|
85
|
+
- Dependencies carry request-scoped concerns: authentication, sessions, pagination, and
|
|
86
|
+
reusable validation. A `yield` dependency owns one resource, cleans up in `finally`, and
|
|
87
|
+
re-raises what it catches.
|
|
88
|
+
- Authentication that appears in OpenAPI uses the security utilities. A JSON Web Token (JWT) verifier allows an
|
|
89
|
+
explicit algorithm list and requires `exp` and `sub`.
|
|
90
|
+
- `BackgroundTasks` runs small follow-up work after the response; durable or heavy work goes to
|
|
91
|
+
a queue. Middleware holds cross-cutting HTTP behavior only, and the last middleware added runs
|
|
92
|
+
outermost.
|
|
93
|
+
- Turn OpenAPI and the interactive docs off unless a typed setting turns them on, and the schema
|
|
94
|
+
exposes no internal or admin routes.
|
|
95
|
+
|
|
96
|
+
## Testing
|
|
97
|
+
|
|
98
|
+
Tests cover the HTTP contract through the test client: status codes, bodies, headers, and
|
|
99
|
+
authentication. Replace external systems with dependency overrides at the application
|
|
100
|
+
boundary. Tests cover the application rather than FastAPI's routing implementation.
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
rules:
|
|
2
|
+
- id: gspot.fastapi.cors-wildcard-with-credentials
|
|
3
|
+
patterns:
|
|
4
|
+
- pattern: $APP.add_middleware(CORSMiddleware, ..., allow_origins=["*"], ...,
|
|
5
|
+
allow_credentials=True, ...)
|
|
6
|
+
message: Every origin is allowed together with credentials, so any site reads an
|
|
7
|
+
authenticated response. List the origins.
|
|
8
|
+
languages:
|
|
9
|
+
- python
|
|
10
|
+
severity: ERROR
|
|
11
|
+
metadata:
|
|
12
|
+
cwe:
|
|
13
|
+
- CWE-942
|
|
14
|
+
category: security
|
|
15
|
+
- id: gspot.fastapi.debug-enabled
|
|
16
|
+
pattern: FastAPI(..., debug=True, ...)
|
|
17
|
+
message: Debug mode returns tracebacks to the client. Read the flag from
|
|
18
|
+
configuration, off by default.
|
|
19
|
+
languages:
|
|
20
|
+
- python
|
|
21
|
+
severity: ERROR
|
|
22
|
+
metadata:
|
|
23
|
+
cwe:
|
|
24
|
+
- CWE-489
|
|
25
|
+
category: security
|
|
26
|
+
- id: gspot.fastapi.exception-text-in-response
|
|
27
|
+
patterns:
|
|
28
|
+
- pattern-either:
|
|
29
|
+
- pattern: HTTPException(..., detail=str($ERROR), ...)
|
|
30
|
+
- pattern: HTTPException(..., detail=f"...{$ERROR}...", ...)
|
|
31
|
+
- pattern-inside: |-
|
|
32
|
+
try:
|
|
33
|
+
...
|
|
34
|
+
except $TYPE as $ERROR:
|
|
35
|
+
...
|
|
36
|
+
message: The text of an internal exception goes to the client. Log it, and
|
|
37
|
+
return a stable message.
|
|
38
|
+
languages:
|
|
39
|
+
- python
|
|
40
|
+
severity: ERROR
|
|
41
|
+
metadata:
|
|
42
|
+
cwe:
|
|
43
|
+
- CWE-209
|
|
44
|
+
category: security
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"recommended": {
|
|
3
|
+
"package": "@darraghor/eslint-plugin-nestjs-typed",
|
|
4
|
+
"version": "7.5.5",
|
|
5
|
+
"source": "configs.flatRecommended",
|
|
6
|
+
"blocks": [
|
|
7
|
+
{ "parser": true },
|
|
8
|
+
{
|
|
9
|
+
"parser": false,
|
|
10
|
+
"rules": {
|
|
11
|
+
"@darraghor/nestjs-typed/provided-injected-should-match-factory-parameters": "error",
|
|
12
|
+
"@darraghor/nestjs-typed/injectable-should-be-provided": [
|
|
13
|
+
"error",
|
|
14
|
+
{ "src": ["src/**/*.ts"], "filterFromPaths": ["node_modules", ".test.", ".spec."] }
|
|
15
|
+
],
|
|
16
|
+
"@darraghor/nestjs-typed/api-property-matches-property-optionality": "error",
|
|
17
|
+
"@darraghor/nestjs-typed/api-method-should-specify-api-response": "error",
|
|
18
|
+
"@darraghor/nestjs-typed/controllers-should-supply-api-tags": "error",
|
|
19
|
+
"@darraghor/nestjs-typed/api-enum-property-best-practices": "error",
|
|
20
|
+
"@darraghor/nestjs-typed/api-property-returning-array-should-set-array": "error",
|
|
21
|
+
"@darraghor/nestjs-typed/validation-pipe-should-use-forbid-unknown": "error",
|
|
22
|
+
"@darraghor/nestjs-typed/param-decorator-name-matches-route-param": "error",
|
|
23
|
+
"@darraghor/nestjs-typed/validated-non-primitive-property-needs-type-decorator": "error",
|
|
24
|
+
"@darraghor/nestjs-typed/validate-nested-of-array-should-set-each": "error",
|
|
25
|
+
"@darraghor/nestjs-typed/all-properties-are-whitelisted": "error",
|
|
26
|
+
"@darraghor/nestjs-typed/all-properties-have-explicit-defined": "error",
|
|
27
|
+
"@darraghor/nestjs-typed/api-methods-should-be-guarded": "off",
|
|
28
|
+
"@darraghor/nestjs-typed/api-method-should-specify-api-operation": "off",
|
|
29
|
+
"@darraghor/nestjs-typed/sort-module-metadata-arrays": "off",
|
|
30
|
+
"@darraghor/nestjs-typed/no-duplicate-decorators": "error",
|
|
31
|
+
"@darraghor/nestjs-typed/use-injectable-provided-token": "error",
|
|
32
|
+
"@darraghor/nestjs-typed/api-property-should-have-api-extra-models": "error",
|
|
33
|
+
"@darraghor/nestjs-typed/api-operation-summary-description-capitalized": "error",
|
|
34
|
+
"@darraghor/nestjs-typed/use-dependency-injection": "off",
|
|
35
|
+
"@darraghor/nestjs-typed/use-correct-endpoint-naming-convention": "off",
|
|
36
|
+
"@darraghor/nestjs-typed/forward-ref-injection-should-use-wrapper-type": "error",
|
|
37
|
+
"@darraghor/nestjs-typed/swagger-file-upload-should-be-documented": "error",
|
|
38
|
+
"@darraghor/nestjs-typed/uploaded-file-should-be-validated": "error"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
"noSwagger": {
|
|
44
|
+
"package": "@darraghor/eslint-plugin-nestjs-typed",
|
|
45
|
+
"version": "7.5.5",
|
|
46
|
+
"source": "configs.flatNoSwagger",
|
|
47
|
+
"blocks": [
|
|
48
|
+
{
|
|
49
|
+
"parser": false,
|
|
50
|
+
"rules": {
|
|
51
|
+
"@darraghor/nestjs-typed/api-property-matches-property-optionality": "off",
|
|
52
|
+
"@darraghor/nestjs-typed/api-method-should-specify-api-response": "off",
|
|
53
|
+
"@darraghor/nestjs-typed/api-method-should-specify-api-operation": "off",
|
|
54
|
+
"@darraghor/nestjs-typed/controllers-should-supply-api-tags": "off",
|
|
55
|
+
"@darraghor/nestjs-typed/api-enum-property-best-practices": "off",
|
|
56
|
+
"@darraghor/nestjs-typed/api-property-returning-array-should-set-array": "off",
|
|
57
|
+
"@darraghor/nestjs-typed/swagger-file-upload-should-be-documented": "off"
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[prefix_collisions]
|
|
2
|
+
kinds = ["controller", "service", "module", "guard", "pipe", "filter", "interceptor", "middleware", "decorator", "gateway", "resolver", "repository", "entity", "dto", "strategy", "provider"]
|
|
3
|
+
|
|
4
|
+
[configuration]
|
|
5
|
+
title = "NestJS"
|
|
6
|
+
requires = ["typescript"]
|
|
7
|
+
suggests = []
|
|
8
|
+
description = "A NestJS service: the compiler options decorators need, the NestJS plugin rules for providers, routes, and DTOs, and no circular modules."
|
|
9
|
+
|
|
10
|
+
[detect]
|
|
11
|
+
dependencies = ["@nestjs/core"]
|
|
12
|
+
filenames = ["nest-cli.json"]
|
|
13
|
+
|
|
14
|
+
[files]
|
|
15
|
+
filenames = ["nest-cli.json"]
|
|
16
|
+
|
|
17
|
+
[[tool]]
|
|
18
|
+
name = "@darraghor/eslint-plugin-nestjs-typed"
|
|
19
|
+
rule_prefix = "@darraghor/nestjs-typed"
|
|
20
|
+
kind = "library"
|
|
21
|
+
version = "7.5.5"
|
|
22
|
+
npm = "@darraghor/eslint-plugin-nestjs-typed"
|
|
23
|
+
|
|
24
|
+
[eslint]
|
|
25
|
+
all_level_rules = ["@darraghor/nestjs-typed/api-operation-summary-description-capitalized"]
|
|
26
|
+
|
|
27
|
+
[eslint.presets.recommended]
|
|
28
|
+
package = "@darraghor/eslint-plugin-nestjs-typed"
|
|
29
|
+
source = "configs.flatRecommended"
|
|
30
|
+
|
|
31
|
+
[eslint.presets.noSwagger]
|
|
32
|
+
package = "@darraghor/eslint-plugin-nestjs-typed"
|
|
33
|
+
source = "configs.flatNoSwagger"
|
|
34
|
+
|
|
35
|
+
[[eslint.rules_off]]
|
|
36
|
+
rules = ["gspot/no-trivial-files", "gspot/no-trivial-functions"]
|
|
37
|
+
files = ["**/*.module.ts"]
|
|
38
|
+
reason = "A Nest module is an empty decorated class that registers providers and controllers, which is the shape Nest asks for."
|
|
39
|
+
|
|
40
|
+
[[eslint.selectors]]
|
|
41
|
+
selector = "CallExpression[callee.name='forwardRef']"
|
|
42
|
+
message = "forwardRef papers over two modules that import each other. Move what both need into a third module."
|
|
43
|
+
|
|
44
|
+
[[eslint.selectors]]
|
|
45
|
+
selector = "Decorator[expression.callee.name=/^(Res|Response)$/]:not(:has(Property[key.name='passthrough'][value.value=true]))"
|
|
46
|
+
message = "Taking the response object turns off interceptors and the return value. Return the value, or pass { passthrough: true }."
|
|
47
|
+
|
|
48
|
+
[[tool_file]]
|
|
49
|
+
target = ".gspot/config/eslint.config.mjs"
|
|
50
|
+
tool = "eslint"
|
|
51
|
+
fragment = true
|
|
52
|
+
required_tools = ["@darraghor/eslint-plugin-nestjs-typed"]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# NestJS
|
|
2
|
+
|
|
3
|
+
These rules cover NestJS modules, controllers, providers, validation, configuration, and tests.
|
|
4
|
+
|
|
5
|
+
## Request handling
|
|
6
|
+
|
|
7
|
+
- Return the documented HTTP status. Do not report a failed operation as a successful response.
|
|
8
|
+
- Validate body, query, and route parameters before using them. Configure `ValidationPipe`
|
|
9
|
+
transformation and unknown-field handling for the declared input contract.
|
|
10
|
+
- Do not expose password hashes, tokens, or internal fields in responses.
|
|
11
|
+
|
|
12
|
+
## Providers and lifecycle
|
|
13
|
+
|
|
14
|
+
- Register each provider with its intended owner. Repeated registration can create distinct instances.
|
|
15
|
+
- Request-scoped dependencies propagate request scope to their consumers. Check lifetime and
|
|
16
|
+
resource costs before selecting that scope.
|
|
17
|
+
- Close connections and timers through the application lifecycle, such as `OnModuleDestroy`.
|
|
18
|
+
|
|
19
|
+
## Configuration and security
|
|
20
|
+
|
|
21
|
+
- Fail startup when required configuration has missing or malformed values.
|
|
22
|
+
- Use guards for route authorization. Enforce additional permissions at each protected operation
|
|
23
|
+
when one request performs several operations or the service has non-HTTP callers.
|
|
24
|
+
- Guards run before pipes. Do not assume guard inputs have passed DTO transformation or validation.
|
|
25
|
+
- Set request-size limits, security headers, and rate limits for the exposed application.
|
|
26
|
+
|
|
27
|
+
## Feature organization
|
|
28
|
+
|
|
29
|
+
<!-- level: all -->
|
|
30
|
+
|
|
31
|
+
- Give each feature one module and export only providers consumed by other features.
|
|
32
|
+
- Inject dependencies through constructors instead of constructing them inside consumers.
|
|
33
|
+
- Define the versioning contract before publishing a public API.
|
|
34
|
+
- Keep request DTOs distinct from persistence entities and map between them explicitly.
|
|
35
|
+
|
|
36
|
+
## Tests
|
|
37
|
+
|
|
38
|
+
- Use `Test.createTestingModule` and `overrideProvider` to isolate service dependencies.
|
|
39
|
+
- Exercise HTTP behavior with the real pipes, guards, and filters.
|
|
40
|
+
- Do not mock the class under test or assert on private methods.
|
|
41
|
+
- Close test applications in teardown.
|
|
42
|
+
|
|
43
|
+
See Nest's [response handling](https://docs.nestjs.com/controllers) and
|
|
44
|
+
[authorization guidance](https://docs.nestjs.com/security/authorization).
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"recommended": {
|
|
3
|
+
"package": "@next/eslint-plugin-next",
|
|
4
|
+
"version": "16.3.8",
|
|
5
|
+
"source": "configs.core-web-vitals",
|
|
6
|
+
"blocks": [
|
|
7
|
+
{
|
|
8
|
+
"parser": false,
|
|
9
|
+
"rules": {
|
|
10
|
+
"@next/next/google-font-display": "warn",
|
|
11
|
+
"@next/next/google-font-preconnect": "warn",
|
|
12
|
+
"@next/next/next-script-for-ga": "warn",
|
|
13
|
+
"@next/next/no-async-client-component": "warn",
|
|
14
|
+
"@next/next/no-before-interactive-script-outside-document": "warn",
|
|
15
|
+
"@next/next/no-css-tags": "warn",
|
|
16
|
+
"@next/next/no-head-element": "warn",
|
|
17
|
+
"@next/next/no-html-link-for-pages": "error",
|
|
18
|
+
"@next/next/no-img-element": "warn",
|
|
19
|
+
"@next/next/no-location-assign-relative-destination": "warn",
|
|
20
|
+
"@next/next/no-page-custom-font": "warn",
|
|
21
|
+
"@next/next/no-styled-jsx-in-document": "warn",
|
|
22
|
+
"@next/next/no-sync-scripts": "error",
|
|
23
|
+
"@next/next/no-title-in-document-head": "warn",
|
|
24
|
+
"@next/next/no-typos": "warn",
|
|
25
|
+
"@next/next/no-unwanted-polyfillio": "warn",
|
|
26
|
+
"@next/next/inline-script-id": "error",
|
|
27
|
+
"@next/next/no-assign-module-variable": "error",
|
|
28
|
+
"@next/next/no-document-import-in-page": "error",
|
|
29
|
+
"@next/next/no-duplicate-head": "error",
|
|
30
|
+
"@next/next/no-head-import-in-document": "error",
|
|
31
|
+
"@next/next/no-script-component-in-head": "error"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"refresh": {
|
|
37
|
+
"package": "eslint-plugin-react-refresh",
|
|
38
|
+
"version": "0.5.7",
|
|
39
|
+
"source": "configs.next",
|
|
40
|
+
"blocks": [
|
|
41
|
+
{
|
|
42
|
+
"parser": false,
|
|
43
|
+
"rules": {
|
|
44
|
+
"react-refresh/only-export-components": [
|
|
45
|
+
"error",
|
|
46
|
+
{
|
|
47
|
+
"allowExportNames": [
|
|
48
|
+
"experimental_ppr",
|
|
49
|
+
"dynamic",
|
|
50
|
+
"dynamicParams",
|
|
51
|
+
"revalidate",
|
|
52
|
+
"fetchCache",
|
|
53
|
+
"runtime",
|
|
54
|
+
"preferredRegion",
|
|
55
|
+
"maxDuration",
|
|
56
|
+
"metadata",
|
|
57
|
+
"generateMetadata",
|
|
58
|
+
"viewport",
|
|
59
|
+
"generateViewport",
|
|
60
|
+
"generateImageMetadata",
|
|
61
|
+
"generateSitemaps",
|
|
62
|
+
"generateStaticParams",
|
|
63
|
+
"instant",
|
|
64
|
+
"contentType",
|
|
65
|
+
"size"
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
route_files = ["page", "layout", "template", "default", "loading", "error", "not-found", "global-error", "route", "middleware", "proxy"]
|
|
2
|
+
allowed_words = ["Next.js"]
|
|
3
|
+
generated = ["next-env.d.ts"]
|
|
4
|
+
|
|
5
|
+
[[naming.override]]
|
|
6
|
+
paths = ["**/app/**", "**/pages/**"]
|
|
7
|
+
categories = ["files"]
|
|
8
|
+
allowed_names = ["instrumentation", "sitemap", "robots", "manifest", "opengraph-image", "icon", "apple-icon", "_app", "_document", "_error", "404", "500"]
|
|
9
|
+
reason = "Next.js names these files."
|
|
10
|
+
|
|
11
|
+
[[naming.override]]
|
|
12
|
+
paths = ["**"]
|
|
13
|
+
languages = ["typescript", "javascript"]
|
|
14
|
+
categories = ["functions", "methods", "variables"]
|
|
15
|
+
allowed_names = ["getStaticProps", "getServerSideProps", "generateMetadata", "generateStaticParams", "generateViewport"]
|
|
16
|
+
reason = "Next.js discovers these data and metadata exports by name."
|
|
17
|
+
|
|
18
|
+
[configuration]
|
|
19
|
+
title = "Next.js"
|
|
20
|
+
requires = ["typescript", "react", "react-dom"]
|
|
21
|
+
suggests = ["css"]
|
|
22
|
+
description = "A Next.js app: the framework and hooks rules of ESLint, and server code marked as server only. Route segments serve one thing, and the configuration turns no build check off."
|
|
23
|
+
|
|
24
|
+
[detect]
|
|
25
|
+
dependencies = ["next"]
|
|
26
|
+
|
|
27
|
+
[files]
|
|
28
|
+
paths = ["**/next.config.js", "**/next.config.mjs", "**/next.config.ts"]
|
|
29
|
+
|
|
30
|
+
[[tool]]
|
|
31
|
+
name = "next"
|
|
32
|
+
system = true
|
|
33
|
+
min_version = "15.5.0"
|
|
34
|
+
version_command = ["--version"]
|
|
35
|
+
|
|
36
|
+
[[tool]]
|
|
37
|
+
name = "@next/eslint-plugin-next"
|
|
38
|
+
rule_prefix = "@next/next"
|
|
39
|
+
kind = "library"
|
|
40
|
+
version = "16.3.8"
|
|
41
|
+
npm = "@next/eslint-plugin-next"
|
|
42
|
+
|
|
43
|
+
[[check]]
|
|
44
|
+
name = "tsc"
|
|
45
|
+
tool = "next"
|
|
46
|
+
example = "The TypeScript declaration `const count: number = \"one\";` reports an incompatible assignment. Correct it to `const count: number = 1;` when the contract requires a number."
|
|
47
|
+
level = "recommended"
|
|
48
|
+
stage = "push"
|
|
49
|
+
runs = "scope"
|
|
50
|
+
instead_of = "typescript/tsc"
|
|
51
|
+
summary = "Has Next.js write its types, then type-checks the app with the strict compiler options."
|
|
52
|
+
why = "A fresh clone holds no next-env.d.ts and no route types, so a plain tsc run fails on imports the framework resolves."
|
|
53
|
+
help = "Read the first error tsc prints and fix that file; later errors are often the same mistake."
|
|
54
|
+
|
|
55
|
+
[[check]]
|
|
56
|
+
name = "build"
|
|
57
|
+
tool = "next"
|
|
58
|
+
example = "A Next.js page importing a nonexistent component fails the project build. Restore the component or correct the import path and rerun the configured build."
|
|
59
|
+
level = "all"
|
|
60
|
+
stage = "push"
|
|
61
|
+
runs = "scope"
|
|
62
|
+
needs = ["build"]
|
|
63
|
+
summary = "Builds the app with next build at all when its scope contains an app or pages tree."
|
|
64
|
+
why = "Some mistakes show only at build time: a server import in a client file, or a page that cannot be prerendered."
|
|
65
|
+
help = "Run next build and read the first error it prints."
|
|
66
|
+
|
|
67
|
+
[check.files]
|
|
68
|
+
paths = ["**/app/**", "**/pages/**"]
|
|
69
|
+
|
|
70
|
+
[[check]]
|
|
71
|
+
name = "route-segments"
|
|
72
|
+
example = "A Next.js segment containing both `page.tsx` and `route.ts` reports conflicting route ownership. Move the route handler into a separate segment, such as `api`, and update its callers."
|
|
73
|
+
level = "recommended"
|
|
74
|
+
stage = "commit"
|
|
75
|
+
runs = "once"
|
|
76
|
+
summary = "Checks that no route segment holds both a page and a route handler."
|
|
77
|
+
why = "This commit check finds route conflicts before next build and also runs at recommended, where the build check does not."
|
|
78
|
+
help = "Move the route handler into a segment of its own, such as an api folder."
|
|
79
|
+
|
|
80
|
+
[[check]]
|
|
81
|
+
name = "next-config"
|
|
82
|
+
example = "Setting `typescript.ignoreBuildErrors` to `true` in Next.js configuration reports a disabled type gate. Remove the switch and correct the type errors before building."
|
|
83
|
+
level = "recommended"
|
|
84
|
+
stage = "commit"
|
|
85
|
+
runs = "once"
|
|
86
|
+
summary = "Checks that the framework configuration turns no build check off and puts no secret into the client environment."
|
|
87
|
+
why = "A build that ignores lint and type errors ships them, and a key under env is readable by every visitor."
|
|
88
|
+
help = "Remove the switch, and read the secret on the server."
|
|
89
|
+
|
|
90
|
+
[[check]]
|
|
91
|
+
name = "version-pairs"
|
|
92
|
+
example = "Different installed versions of `next` and its lint package report an alignment finding. Install the same version of both and update the lockfile with the package manager."
|
|
93
|
+
level = "recommended"
|
|
94
|
+
stage = "commit"
|
|
95
|
+
runs = "once"
|
|
96
|
+
summary = "Checks that Next.js and its lint packages use the same installed version."
|
|
97
|
+
why = "A lint package a version behind flags what the framework allows and allows what it removed."
|
|
98
|
+
help = "Move both packages to the same version."
|
|
99
|
+
|
|
100
|
+
[eslint]
|
|
101
|
+
all_level_rules = ["@next/next/next-script-for-ga", "@next/next/no-html-link-for-pages", "@next/next/no-img-element", "@next/next/no-page-custom-font"]
|
|
102
|
+
|
|
103
|
+
[eslint.presets.recommended]
|
|
104
|
+
package = "@next/eslint-plugin-next"
|
|
105
|
+
source = "configs.core-web-vitals"
|
|
106
|
+
|
|
107
|
+
[eslint.presets.refresh]
|
|
108
|
+
package = "eslint-plugin-react-refresh"
|
|
109
|
+
source = "configs.next"
|
|
110
|
+
|
|
111
|
+
[[eslint.rules_off]]
|
|
112
|
+
rules = ["gspot/no-trivial-files"]
|
|
113
|
+
reason = "Next.js discovers route files by name, so each file can hold one export in its own folder."
|
|
114
|
+
|
|
115
|
+
[[tool_file]]
|
|
116
|
+
target = ".gspot/config/eslint.config.mjs"
|
|
117
|
+
tool = "eslint"
|
|
118
|
+
fragment = true
|
|
119
|
+
required_tools = ["@next/eslint-plugin-next"]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Next.js
|
|
2
|
+
|
|
3
|
+
Preserve the project's router, installed Next.js and React versions, runtime, and deployment
|
|
4
|
+
adapter. App Router guidance applies only to App Router routes.
|
|
5
|
+
|
|
6
|
+
## Server and client ownership
|
|
7
|
+
|
|
8
|
+
Keep privileged operations and credentials on the server. Send only authorized fields to the
|
|
9
|
+
browser. That includes query hydration and Server Component props. A hidden control, type, or filename
|
|
10
|
+
does not control access. A client entry includes its imports in the client graph; keep it small
|
|
11
|
+
and compose server-rendered children into it.
|
|
12
|
+
|
|
13
|
+
Validate and authorize every mutation at its server entry. Routes, actions, and RPC procedures
|
|
14
|
+
share the same domain operation. Initial server reads do not HTTP-fetch the application's own
|
|
15
|
+
Route Handler. Rendering, `GET` requests, and prefetching perform no writes.
|
|
16
|
+
|
|
17
|
+
See [Server and Client Components](https://nextjs.org/docs/app/getting-started/server-and-client-components).
|
|
18
|
+
|
|
19
|
+
## Loading and failures
|
|
20
|
+
|
|
21
|
+
Put a loading boundary around the section that waits. Keep useful content mounted during refresh,
|
|
22
|
+
and reset pending state when the selected resource changes. Start independent work early without
|
|
23
|
+
moving protected reads before authorization.
|
|
24
|
+
|
|
25
|
+
Decide status, headers, cookies, redirects, and not-found behavior before streaming commits the
|
|
26
|
+
response. Preserve framework control-flow exceptions. Check direct load, client navigation,
|
|
27
|
+
refresh, Back, and failed prefetch; those paths need not share the same state.
|
|
28
|
+
|
|
29
|
+
See [streaming](https://nextjs.org/docs/app/guides/streaming).
|
|
30
|
+
|
|
31
|
+
## Cache contracts
|
|
32
|
+
|
|
33
|
+
Check whether the project enables Cache Components before choosing APIs. Above a cached operation,
|
|
34
|
+
state who may read it, its key, lifetime, and invalidation path. Include identity, tenant, locale,
|
|
35
|
+
filters, and permissions where they affect results; session tokens stay out of keys.
|
|
36
|
+
|
|
37
|
+
Default private data to uncached until an isolation design exists. Never let a cached decision
|
|
38
|
+
retain revoked access. Clear private browser caches on logout. Invalidate after the write commits
|
|
39
|
+
and before a terminating redirect; a failed refresh must not retry the committed write.
|
|
40
|
+
Next.js and a client query cache need separate reconciliation.
|
|
41
|
+
|
|
42
|
+
See [caching](https://nextjs.org/docs/app/getting-started/caching)
|
|
43
|
+
and [revalidation](https://nextjs.org/docs/app/getting-started/revalidating).
|