requestshield 0.1.4 → 0.1.5

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.
@@ -1,389 +1,389 @@
1
- # Integration planning
2
-
3
- Use this reference before writing or removing RequestShield integration code. It owns the
4
- planning steps that are too detailed for `SKILL.md`.
5
-
6
- ## Workflow by operation
7
-
8
- - **Install** -> `integration-planning.md` full flow: **Credentials check** ->
9
- **Detect existing integration** -> **Contract check** -> **Gate** -> **Then act on
10
- what you found** -> **Choosing the mode** -> **Installing** -> **Verifying** ->
11
- **Negative test**.
12
- - **Verify / Troubleshoot** -> `integration-planning.md` (Detect existing integration
13
- only) -> **Verifying**. If a reason code, degraded result, browser mode, backend path,
14
- or unknown state is involved, read only the matching reference files.
15
- - **Uninstall** -> `integration-planning.md` (Detect existing integration only) ->
16
- **Uninstalling**.
17
-
18
- Do not couple browser mode to backend path. They are independent axes:
19
-
20
- - Browser mode: Seamless (`browser-seamless.md`) or Manual (`browser-manual.md`).
21
- - Backend path: Spring Boot 3 starter (`backend-spring-boot.md`) or Java core SDK
22
- (`backend-java-core.md`).
23
-
24
- Either browser mode may pair with either supported backend path. The backend stack
25
- selects the backend path. The way the browser issues the protected request selects the
26
- browser mode.
27
-
28
- ## Credentials check
29
-
30
- The **pre-edit** check: confirm a credential source exists before touching backend
31
- integration code. This step belongs to the Install full flow.
32
-
33
- - **Install** — always, before editing backend code.
34
- - **Verify / Troubleshoot** — do not run this as part of the operation route. Start with
35
- **Detect existing integration only**, then use `troubleshooting.md` if the evidence
36
- points to a credential failure.
37
- - **Uninstall** — do not run this step; detect the existing integration only.
38
-
39
- Diagnosing a credential *failure* is a different question, and `troubleshooting.md`
40
- owns it — go there for `consume_unauthorized`, `unknown_customer`, or a backend
41
- rejecting every token, whether or not this check has already passed.
42
-
43
- RequestShield requires:
44
-
45
- - an App Key
46
- - an API Secret
47
-
48
- Check for credential sources without printing, reading, or echoing their values. Confirm
49
- presence only; never print, cat, or echo either value:
50
-
51
- ```bash
52
- # Presence check only. Never print secret values.
53
-
54
- if env | grep -Eq '^(INTELLIFEND_)?REQUESTSHIELD_(APP_KEY|KEY)='; then
55
- echo "RequestShield App Key: present"
56
- else
57
- echo "RequestShield App Key: not found in current environment"
58
- fi
59
-
60
- if env | grep -Eq '^(INTELLIFEND_)?REQUESTSHIELD_(API_SECRET|SECRET)='; then
61
- echo "RequestShield API Secret: present"
62
- else
63
- echo "RequestShield API Secret: not found in current environment"
64
- fi
65
- ```
66
-
67
- Common variable names are `INTELLIFEND_REQUESTSHIELD_APP_KEY`,
68
- `REQUESTSHIELD_APP_KEY`, `REQUESTSHIELD_KEY`,
69
- `INTELLIFEND_REQUESTSHIELD_API_SECRET`, `REQUESTSHIELD_API_SECRET`, and
70
- `REQUESTSHIELD_SECRET`. A matching non-canonical name is only a credential candidate.
71
- Confirm that the backend actually reads or maps that variable before treating it as a
72
- valid RequestShield credential.
73
-
74
- If the App Key is known, also confirm server-side credential state without exposing the
75
- secret:
76
-
77
- ```bash
78
- requestshield credentials status <app-key>
79
- ```
80
-
81
- A credential that is not present in the current shell is not necessarily missing from
82
- the deployed backend. It may be injected at runtime by a secret manager, CI/CD system,
83
- container runtime, Kubernetes Secret, or deployment environment.
84
-
85
- Report only what was confirmed:
86
-
87
- | Finding | Action |
88
- | --- | --- |
89
- | Both credential sources are confirmed | Continue. Never read their values. |
90
- | A non-canonical variable is found and its backend mapping is confirmed | Report the mapping and continue. |
91
- | Credentials are provisioned, but the runtime source is unknown | Ask how the deployed backend receives them before editing backend integration code. |
92
- | No credential source can be confirmed | Report the uncertainty and ask before proceeding. |
93
- | No credential pair has been provisioned | Ask the user to provision credentials before continuing with backend setup. |
94
-
95
- If no pair has been provisioned yet, the user creates it. You may run the read-only
96
- `requestshield apps list` to see whether an application already exists. Everything that
97
- mints or changes a secret stays with the user; see **Key and secret management** in
98
- `SKILL.md`.
99
-
100
- ## Detect existing integration
101
-
102
- Search for existing RequestShield code before install, verify/troubleshoot, or uninstall.
103
- Use `rg` when available. Use `rg -l` for filename-only discovery.
104
-
105
-
106
- Browser markers:
107
-
108
- ```bash
109
- rg -l "data-app-key|data-protect|IntelliFend|X-IntelliFend-Token" \
110
- -g "*.html" -g "*.htm" -g "*.js" -g "*.jsx" -g "*.ts" -g "*.tsx" \
111
- -g "*.vue" -g "*.svelte" -g "*.astro" \
112
- -g "*.erb" -g "*.haml" -g "*.php" -g "*.jinja*" -g "*.j2" -g "*.twig" \
113
- -g "*.hbs" -g "*.ejs" -g "*.pug" -g "*.blade.php" \
114
- -g "!node_modules/**" -g "!**/skills/**" -g "!**/.requestshield/**" .
115
- ```
116
-
117
- The tag often lives in a server-rendered layout rather than a static `.html` — a Django,
118
- Rails, Laravel, or Thymeleaf base template — so a scan limited to `.html`/`.js` reports
119
- "not integrated" on a codebase that already has one, and the install then adds a second
120
- tag. If the app's templates use an extension not listed above, add it before concluding.
121
-
122
- Backend markers:
123
-
124
- ```bash
125
- rg -l "RequestShieldClient|RequestShieldProtected|intellifend|requestshield" \
126
- -g "*.java" -g "*.kt" -g "*.xml" -g "*.yml" -g "*.yaml" \
127
- -g "*.gradle" -g "*.gradle.kts" \
128
- -g "!**/skills/**" -g "!**/.requestshield/**" .
129
- ```
130
-
131
- `rg -l` returns candidate filenames only. Do not follow it with `cat`, an unrestricted
132
- `sed`, plain `rg -n` without `-o`, or another command that exposes complete matching
133
- lines or the entire file.
134
-
135
- Locate known markers without returning the rest of their lines:
136
-
137
- ```bash
138
- rg -n -o \
139
- "data-app-key|data-protect|IntelliFend|getToken|X-IntelliFend-Token" \
140
- <browser-candidate-file>
141
-
142
- rg -n -o \
143
- "RequestShieldClient|RequestShieldProtected|verify|isAllowed|intellifend|requestshield" \
144
- <backend-candidate-file>
145
-
146
- rg -n -o \
147
- "(INTELLIFEND_)?REQUESTSHIELD_(APP_KEY|KEY|API_SECRET|SECRET)" \
148
- <candidate-configuration-file>
149
- ```
150
- -o prints only the matched marker or environment-variable name, not the surrounding
151
- value. Environment-variable names are safe to report; their values are not.
152
-
153
- When configuration context is required, inspect it only through a secret-aware parser
154
- or redacted view that masks sensitive values before stdout reaches the agent. Its output
155
- may contain the filename, line number, configuration key, environment-variable name,
156
- App Key when needed to identify the integration, and whether the source is an
157
- environment reference, secret-manager reference, or literal.
158
-
159
- Replace every API Secret, RequestShield token, session value, CSRF value,
160
- authorization credential, and other credential literal with <redacted>.
161
-
162
- If no secret-aware parser or redacted view is available, do not read the candidate
163
- configuration file verbatim. Ask the user for a sanitized excerpt instead. Switching
164
- to rg -l and then reading the entire candidate file is not safe.
165
-
166
- The exclusions matter when this skill is installed inside the repo being scanned —
167
- browser and backend reference files contain marker strings and would otherwise read as
168
- an existing integration.
169
-
170
- Report one of:
171
-
172
- - **Not integrated**: no browser or backend markers.
173
- - **Browser only**: tokens may be obtained or attached, but no backend enforcement was
174
- found. This looks protected and is not.
175
- - **Backend only**: enforcement exists, but no page sends a token. In BLOCK mode this can
176
- reject legitimate users.
177
- - **Fully integrated**: browser mode and backend path are both present. Name both.
178
-
179
- If a script tag already exists, update it instead of adding a second one. The SDK
180
- initializes from `document.currentScript`; two tags mean two configurations, and the
181
- last one to run wins rather than the two merging.
182
-
183
- ## Contract check
184
-
185
- Run this **after** detection and **before** the Gate: detection says what the codebase
186
- already does, the contract says what the platform currently allows, and the browser-mode
187
- decision needs both.
188
-
189
- ```bash
190
- requestshield contract
191
- ```
192
-
193
- It is authoritative for `browser.script_url`, `browser.token_header`,
194
- `browser.available_modes`, `backend.supported_languages`, and `backend.min_jdk`. Use
195
- those values verbatim; a stale hardcoded script URL produces a page that loads nothing
196
- and fails silently.
197
-
198
- Treat every array as an **allow-list**. There is no "unavailable" flag, so something is
199
- unavailable when it is absent — and an absent, empty, or unparseable array is
200
- unavailable, not permission. If the contract is missing or returns `ok: false`, stop and
201
- report what the CLI returned.
202
-
203
- ### Backend consequences — these feed the Gate
204
-
205
- - `backend.supported_languages` missing the customer's backend language → **stop.** No
206
- backend path exists, and a browser-only install protects nothing.
207
- - `backend.min_jdk` above the project's Java target → **stop.** The JDK upgrade is the
208
- prerequisite; do not fall back to an older SDK version.
209
-
210
- ### Browser-mode consequences — these decide the mode
211
-
212
- Read `browser.available_modes` together with what detection found in the codebase:
213
-
214
- | Released modes | Codebase today | Do this |
215
- | --- | --- | --- |
216
- | **One mode** | already uses that mode | Keep it. Nothing to change on the browser side. |
217
- | **One mode** | uses the *other* mode | **This is a finding, not a preference.** The mode in the codebase is not released, so it cannot be relied on. Report it and migrate the client to the released mode. |
218
- | **One mode** | not integrated | No choice to make. Install that mode, and say which one and why. |
219
- | **Both modes** | already uses one of them | **Keep the mode already in use.** Change it only for a concrete reason — the transport is one Seamless cannot cover, or the user asks. A mode switch rewrites working call sites and risks leaving an endpoint half-migrated. |
220
- | **Both modes** | not integrated | A genuine choice. Go to **Choosing the mode** below. |
221
- | **Neither** | any | **Stop.** No browser mode is released; report the contract output. |
222
-
223
- Per-endpoint, not per-repo: a page may legitimately run Seamless on one endpoint and
224
- Manual on another. Apply the table to the operation being worked on, not to the codebase
225
- as a whole.
226
-
227
- ## Gate
228
-
229
- The backend path is decided by the backend stack, not by browser preference. Establish
230
- this before proposing edits. Discovering a backend blocker after the browser half is
231
- written leaves tokens flowing with nothing enforcing them.
232
-
233
- An integration is one backend path plus one browser mode, and they are independent
234
- choices. A non-Spring-MVC backend does not rule out Seamless mode; it only rules out the
235
- Spring Boot 3 starter backend path.
236
-
237
- Backend path:
238
-
239
- | Backend path | Requirement | Reference |
240
- | --- | --- | --- |
241
- | **Spring Boot 3 starter** | Spring Boot 3 with Spring MVC, and Java 17+ | `backend-spring-boot.md` |
242
- | **Java core SDK** | Java 17+ | `backend-java-core.md` |
243
-
244
- Browser mode:
245
-
246
- | Browser mode | Use when | Reference |
247
- | --- | --- | --- |
248
- | **Seamless** | The page reaches the endpoint with `fetch` or async `XMLHttpRequest` | `browser-seamless.md` |
249
- | **Manual** | The call needs explicit token timing, or a non-header carrier | `browser-manual.md` |
250
-
251
- Either browser mode pairs with either backend path. The token travels in
252
- `X-IntelliFend-Token` regardless. Java below 17 blocks both backend paths, and no amount
253
- of browser-side work substitutes for backend enforcement.
254
-
255
- ### Run the check
256
-
257
- Find the build file for the module that serves the protected endpoint, not the first one
258
- in the tree. In a monorepo, locate the route first:
259
-
260
- ```bash
261
- find . -maxdepth 4 \( -name pom.xml -o -name "build.gradle*" \) \
262
- -not -path "*/node_modules/*" -not -path "*/build/*" -not -path "*/target/*"
263
- rg -n "/api/register" -g "*.java" -g "*.kt" .
264
- ```
265
-
266
- One build file, inspect it. Several, and the route cannot be tied to one of them — or
267
- several backends could plausibly serve it — **stop and ask which service owns the
268
- endpoint.** Two backends in a monorepo can differ in both Java version and framework, so
269
- a guess here invalidates the whole integration rather than needing a tweak.
270
-
271
- Use repo-wide build-file scans only for discovery. Do not combine a route found in one
272
- module with Java or Spring evidence from another module.
273
-
274
- #### Java target version
275
-
276
- Read the *project's target*, never the machine's JDK.
277
-
278
- ```bash
279
- rg -n "maven\.compiler\.(release|source|target)|<release>|<java\.version>" <owning-pom.xml>
280
- rg -n "languageVersion|sourceCompatibility|jvmToolchain|JavaVersion" <owning-build.gradle*>
281
- ```
282
-
283
- - **Maven** — `maven.compiler.release`, or the compiler plugin's `<release>`, is
284
- authoritative. Spring Boot's parent POM exposes `<java.version>`, which is the
285
- idiomatic place in a Boot app. **Check the parent POM too** when the module inherits;
286
- the value is often not in the module's own file.
287
- - **Gradle** — `java.toolchain.languageVersion = JavaLanguageVersion.of(21)` and
288
- `kotlin { jvmToolchain(21) }` are both authoritative, and both easy to miss because
289
- they sit inside a block rather than on a property line.
290
- - **When the build file says nothing** — `.java-version`, `.sdkmanrc`, `.tool-versions`,
291
- a `setup-java` step's `java-version` in `.github/workflows/`, or a JDK base image in a
292
- `Dockerfile`. These are **hints, not answers**: they describe an environment, which can
293
- differ from what the build compiles against. A build file that yields only hints is the
294
- ambiguous row below — ask.
295
- - `java -version` reports the **local** JDK. A repo targeting 11 builds fine on a machine
296
- with 21 installed, so it is a last-resort hint and never evidence.
297
-
298
- Java below 17 blocks **both** backend paths. Report the version and its source and name
299
- the upgrade as the prerequisite; do not fall back to an older SDK version or write a
300
- partial browser-only integration.
301
-
302
- #### Spring Boot 3, and separately Spring MVC
303
-
304
- Two conditions. Both must hold for the Spring Boot starter.
305
-
306
- ```bash
307
- rg -n "spring-boot-starter-parent|spring-boot-dependencies|org\.springframework\.boot" \
308
- <owning-pom.xml-or-build.gradle*> | rg "3\.[0-9]+"
309
- rg -n "spring-boot-starter-web[\"' :<]|spring-boot-starter-webflux|spring-webmvc" \
310
- <owning-pom.xml-or-build.gradle*>
311
- ```
312
-
313
- Read the parent version, the `spring-boot-dependencies` BOM version, or the Gradle plugin
314
- version. **Spring Boot 2.x does not qualify** — the starter is a Boot 3 artifact, so a
315
- 2.x app needs the core SDK path even though it is Spring.
316
-
317
- Then MVC versus WebFlux — the trap worth checking every time:
318
-
319
- - `spring-boot-starter-web` → Spring MVC. The starter is available when the app is also
320
- Spring Boot 3 on Java 17+.
321
- - `spring-boot-starter-webflux` with no `-web` → **not** Spring MVC. The starter protects
322
- Spring MVC handler methods, so `@RequestShieldProtected` does not apply. Such an app is
323
- Spring Boot 3 on Java 17+ and still needs the Java core SDK — say so explicitly,
324
- because "Spring Boot 3, Java 21" reads like a starter green light and quietly is not.
325
- Browser mode is unaffected; Seamless remains available when the request transport
326
- matches.
327
- - **Both present** → determine which stack serves the protected route. A
328
- `@RestController` returning `Mono`/`Flux`, or a `RouterFunction`, indicates the
329
- reactive path. Ask if it stays unclear.
330
-
331
- Also not Spring MVC, and therefore the Java core SDK backend path: **Quarkus, Micronaut,
332
- Dropwizard, Helidon, Ktor, Vert.x, Jakarta EE / JAX-RS on its own, and a plain servlet
333
- application.**
334
-
335
- ## Then act on what you found
336
-
337
- This decision chooses the backend path only. Pick the browser mode separately from how
338
- the page issues the protected request.
339
-
340
- | Detected | Do this |
341
- | --- | --- |
342
- | Java < 17, or no Java backend | Stop before editing. Report the finding and its source; no backend path is available, so nothing can enforce. Name the upgrade as the prerequisite. |
343
- | Java 17+, no Spring Boot 3 MVC | Use Java core SDK. It is the only backend option; state it rather than asking a fake choice. |
344
- | Java 17+ and Spring Boot 3 MVC | Both backend paths are viable. Recommend the starter and confirm once. |
345
- | Ambiguous module, missing build file, version only in environment hints, or unclear MVC/WebFlux ownership | Ask, showing what you found. |
346
-
347
- Always report the evidence with file and line references:
348
-
349
- ```text
350
- Backend: theair-customer-backend/build.gradle.kts
351
- Java 21 - java.toolchain.languageVersion (line 14)
352
- Spring Boot 3.2.1 - org.springframework.boot plugin (line 3)
353
- Spring MVC - spring-boot-starter-web (line 20)
354
- Backend path: Spring Boot 3 starter. Browser mode still depends on released modes and request transport.
355
- ```
356
-
357
- Detection is inference from build files. If any line would read "unknown", ask rather
358
- than assume.
359
-
360
- ## Choosing the mode
361
-
362
- Reach this section only when **Contract check** left a genuine choice: both modes
363
- released, and this operation not already integrated. If it landed on any other row of
364
- that table, the mode is already decided — do not re-open it here.
365
-
366
- This is the browser half. Backend path does not constrain it. Both browser modes deliver
367
- the token in `X-IntelliFend-Token`, which either backend path reads.
368
-
369
- | | **Seamless** | **Manual** |
370
- | --- | --- | --- |
371
- | Browser | `data-protect` lists exact endpoints; the SDK attaches tokens to matching `fetch` and async XHR requests. | `await IntelliFend.getToken()` at each protected call site, attached in application code. |
372
- | Requires | The request travels by `fetch` or async XHR from page scope. | Nothing beyond the SDK and a supported carrier. |
373
- | Code touched | One line of HTML. | Every protected call site, plus every retry path. |
374
- | Backend work | Identical either way: whichever backend path the Gate selected. | Identical either way: whichever backend path the Gate selected. |
375
-
376
- Released-mode availability was already settled at **Contract check**. One question is
377
- left: does the protected request travel by `fetch` or asynchronous `XMLHttpRequest` from
378
- page scope? Native form POST, `sendBeacon`, WebSocket/EventSource, synchronous XHR,
379
- `no-cors`, and service-worker-owned requests are not intercepted — read
380
- `browser-seamless.md` for the full coverage list.
381
-
382
- Recommend Seamless when that check passes. It touches one line instead of every call
383
- site, so there is no missed call site or retry path. If it fails, use Manual and give
384
- the reason; that is a finding, not a preference.
385
-
386
- A given protected operation uses one mode, not both. Different endpoints on the same
387
- page may use different modes when necessary. Never use two backend verification paths on
388
- one request; `@RequestShieldProtected` and manual `RequestShieldClient.verify()` both
389
- consume the token, so the second returns `token_replayed`.
1
+ # Integration planning
2
+
3
+ Use this reference before writing or removing RequestShield integration code. It owns the
4
+ planning steps that are too detailed for `SKILL.md`.
5
+
6
+ ## Workflow by operation
7
+
8
+ - **Install** -> `integration-planning.md` full flow: **Credentials check** ->
9
+ **Detect existing integration** -> **Contract check** -> **Gate** -> **Then act on
10
+ what you found** -> **Choosing the mode** -> **Installing** -> **Verifying** ->
11
+ **Negative test**.
12
+ - **Verify / Troubleshoot** -> `integration-planning.md` (Detect existing integration
13
+ only) -> **Verifying**. If a reason code, degraded result, browser mode, backend path,
14
+ or unknown state is involved, read only the matching reference files.
15
+ - **Uninstall** -> `integration-planning.md` (Detect existing integration only) ->
16
+ **Uninstalling**.
17
+
18
+ Do not couple browser mode to backend path. They are independent axes:
19
+
20
+ - Browser mode: Seamless (`browser-seamless.md`) or Manual (`browser-manual.md`).
21
+ - Backend path: Spring Boot 3 starter (`backend-spring-boot.md`) or Java core SDK
22
+ (`backend-java-core.md`).
23
+
24
+ Either browser mode may pair with either supported backend path. The backend stack
25
+ selects the backend path. The way the browser issues the protected request selects the
26
+ browser mode.
27
+
28
+ ## Credentials check
29
+
30
+ The **pre-edit** check: confirm a credential source exists before touching backend
31
+ integration code. This step belongs to the Install full flow.
32
+
33
+ - **Install** — always, before editing backend code.
34
+ - **Verify / Troubleshoot** — do not run this as part of the operation route. Start with
35
+ **Detect existing integration only**, then use `troubleshooting.md` if the evidence
36
+ points to a credential failure.
37
+ - **Uninstall** — do not run this step; detect the existing integration only.
38
+
39
+ Diagnosing a credential *failure* is a different question, and `troubleshooting.md`
40
+ owns it — go there for `consume_unauthorized`, `unknown_customer`, or a backend
41
+ rejecting every token, whether or not this check has already passed.
42
+
43
+ RequestShield requires:
44
+
45
+ - an App Key
46
+ - an API Secret
47
+
48
+ Check for credential sources without printing, reading, or echoing their values. Confirm
49
+ presence only; never print, cat, or echo either value:
50
+
51
+ ```bash
52
+ # Presence check only. Never print secret values.
53
+
54
+ if env | grep -Eq '^(INTELLIFEND_)?REQUESTSHIELD_(APP_KEY|KEY)='; then
55
+ echo "RequestShield App Key: present"
56
+ else
57
+ echo "RequestShield App Key: not found in current environment"
58
+ fi
59
+
60
+ if env | grep -Eq '^(INTELLIFEND_)?REQUESTSHIELD_(API_SECRET|SECRET)='; then
61
+ echo "RequestShield API Secret: present"
62
+ else
63
+ echo "RequestShield API Secret: not found in current environment"
64
+ fi
65
+ ```
66
+
67
+ Common variable names are `INTELLIFEND_REQUESTSHIELD_APP_KEY`,
68
+ `REQUESTSHIELD_APP_KEY`, `REQUESTSHIELD_KEY`,
69
+ `INTELLIFEND_REQUESTSHIELD_API_SECRET`, `REQUESTSHIELD_API_SECRET`, and
70
+ `REQUESTSHIELD_SECRET`. A matching non-canonical name is only a credential candidate.
71
+ Confirm that the backend actually reads or maps that variable before treating it as a
72
+ valid RequestShield credential.
73
+
74
+ If the App Key is known, also confirm server-side credential state without exposing the
75
+ secret:
76
+
77
+ ```bash
78
+ requestshield credentials status <app-key>
79
+ ```
80
+
81
+ A credential that is not present in the current shell is not necessarily missing from
82
+ the deployed backend. It may be injected at runtime by a secret manager, CI/CD system,
83
+ container runtime, Kubernetes Secret, or deployment environment.
84
+
85
+ Report only what was confirmed:
86
+
87
+ | Finding | Action |
88
+ | --- | --- |
89
+ | Both credential sources are confirmed | Continue. Never read their values. |
90
+ | A non-canonical variable is found and its backend mapping is confirmed | Report the mapping and continue. |
91
+ | Credentials are provisioned, but the runtime source is unknown | Ask how the deployed backend receives them before editing backend integration code. |
92
+ | No credential source can be confirmed | Report the uncertainty and ask before proceeding. |
93
+ | No credential pair has been provisioned | Ask the user to provision credentials before continuing with backend setup. |
94
+
95
+ If no pair has been provisioned yet, the user creates it. You may run the read-only
96
+ `requestshield apps list` to see whether an application already exists. Everything that
97
+ mints or changes a secret stays with the user; see **Key and secret management** in
98
+ `SKILL.md`.
99
+
100
+ ## Detect existing integration
101
+
102
+ Search for existing RequestShield code before install, verify/troubleshoot, or uninstall.
103
+ Use `rg` when available. Use `rg -l` for filename-only discovery.
104
+
105
+
106
+ Browser markers:
107
+
108
+ ```bash
109
+ rg -l "data-app-key|data-protect|IntelliFend|X-IntelliFend-Token" \
110
+ -g "*.html" -g "*.htm" -g "*.js" -g "*.jsx" -g "*.ts" -g "*.tsx" \
111
+ -g "*.vue" -g "*.svelte" -g "*.astro" \
112
+ -g "*.erb" -g "*.haml" -g "*.php" -g "*.jinja*" -g "*.j2" -g "*.twig" \
113
+ -g "*.hbs" -g "*.ejs" -g "*.pug" -g "*.blade.php" \
114
+ -g "!node_modules/**" -g "!**/skills/**" -g "!**/.requestshield/**" .
115
+ ```
116
+
117
+ The tag often lives in a server-rendered layout rather than a static `.html` — a Django,
118
+ Rails, Laravel, or Thymeleaf base template — so a scan limited to `.html`/`.js` reports
119
+ "not integrated" on a codebase that already has one, and the install then adds a second
120
+ tag. If the app's templates use an extension not listed above, add it before concluding.
121
+
122
+ Backend markers:
123
+
124
+ ```bash
125
+ rg -l "RequestShieldClient|RequestShieldProtected|intellifend|requestshield" \
126
+ -g "*.java" -g "*.kt" -g "*.xml" -g "*.yml" -g "*.yaml" \
127
+ -g "*.gradle" -g "*.gradle.kts" \
128
+ -g "!**/skills/**" -g "!**/.requestshield/**" .
129
+ ```
130
+
131
+ `rg -l` returns candidate filenames only. Do not follow it with `cat`, an unrestricted
132
+ `sed`, plain `rg -n` without `-o`, or another command that exposes complete matching
133
+ lines or the entire file.
134
+
135
+ Locate known markers without returning the rest of their lines:
136
+
137
+ ```bash
138
+ rg -n -o \
139
+ "data-app-key|data-protect|IntelliFend|getToken|X-IntelliFend-Token" \
140
+ <browser-candidate-file>
141
+
142
+ rg -n -o \
143
+ "RequestShieldClient|RequestShieldProtected|verify|isAllowed|intellifend|requestshield" \
144
+ <backend-candidate-file>
145
+
146
+ rg -n -o \
147
+ "(INTELLIFEND_)?REQUESTSHIELD_(APP_KEY|KEY|API_SECRET|SECRET)" \
148
+ <candidate-configuration-file>
149
+ ```
150
+ -o prints only the matched marker or environment-variable name, not the surrounding
151
+ value. Environment-variable names are safe to report; their values are not.
152
+
153
+ When configuration context is required, inspect it only through a secret-aware parser
154
+ or redacted view that masks sensitive values before stdout reaches the agent. Its output
155
+ may contain the filename, line number, configuration key, environment-variable name,
156
+ App Key when needed to identify the integration, and whether the source is an
157
+ environment reference, secret-manager reference, or literal.
158
+
159
+ Replace every API Secret, RequestShield token, session value, CSRF value,
160
+ authorization credential, and other credential literal with <redacted>.
161
+
162
+ If no secret-aware parser or redacted view is available, do not read the candidate
163
+ configuration file verbatim. Ask the user for a sanitized excerpt instead. Switching
164
+ to rg -l and then reading the entire candidate file is not safe.
165
+
166
+ The exclusions matter when this skill is installed inside the repo being scanned —
167
+ browser and backend reference files contain marker strings and would otherwise read as
168
+ an existing integration.
169
+
170
+ Report one of:
171
+
172
+ - **Not integrated**: no browser or backend markers.
173
+ - **Browser only**: tokens may be obtained or attached, but no backend enforcement was
174
+ found. This looks protected and is not.
175
+ - **Backend only**: enforcement exists, but no page sends a token. In BLOCK mode this can
176
+ reject legitimate users.
177
+ - **Fully integrated**: browser mode and backend path are both present. Name both.
178
+
179
+ If a script tag already exists, update it instead of adding a second one. The SDK
180
+ initializes from `document.currentScript`; two tags mean two configurations, and the
181
+ last one to run wins rather than the two merging.
182
+
183
+ ## Contract check
184
+
185
+ Run this **after** detection and **before** the Gate: detection says what the codebase
186
+ already does, the contract says what the platform currently allows, and the browser-mode
187
+ decision needs both.
188
+
189
+ ```bash
190
+ requestshield contract
191
+ ```
192
+
193
+ It is authoritative for `browser.script_url`, `browser.token_header`,
194
+ `browser.available_modes`, `backend.supported_languages`, and `backend.min_jdk`. Use
195
+ those values verbatim; a stale hardcoded script URL produces a page that loads nothing
196
+ and fails silently.
197
+
198
+ Treat every array as an **allow-list**. There is no "unavailable" flag, so something is
199
+ unavailable when it is absent — and an absent, empty, or unparseable array is
200
+ unavailable, not permission. If the contract is missing or returns `ok: false`, stop and
201
+ report what the CLI returned.
202
+
203
+ ### Backend consequences — these feed the Gate
204
+
205
+ - `backend.supported_languages` missing the customer's backend language → **stop.** No
206
+ backend path exists, and a browser-only install protects nothing.
207
+ - `backend.min_jdk` above the project's Java target → **stop.** The JDK upgrade is the
208
+ prerequisite; do not fall back to an older SDK version.
209
+
210
+ ### Browser-mode consequences — these decide the mode
211
+
212
+ Read `browser.available_modes` together with what detection found in the codebase:
213
+
214
+ | Released modes | Codebase today | Do this |
215
+ | --- | --- | --- |
216
+ | **One mode** | already uses that mode | Keep it. Nothing to change on the browser side. |
217
+ | **One mode** | uses the *other* mode | **This is a finding, not a preference.** The mode in the codebase is not released, so it cannot be relied on. Report it and migrate the client to the released mode. |
218
+ | **One mode** | not integrated | No choice to make. Install that mode, and say which one and why. |
219
+ | **Both modes** | already uses one of them | **Keep the mode already in use.** Change it only for a concrete reason — the transport is one Seamless cannot cover, or the user asks. A mode switch rewrites working call sites and risks leaving an endpoint half-migrated. |
220
+ | **Both modes** | not integrated | A genuine choice. Go to **Choosing the mode** below. |
221
+ | **Neither** | any | **Stop.** No browser mode is released; report the contract output. |
222
+
223
+ Per-endpoint, not per-repo: a page may legitimately run Seamless on one endpoint and
224
+ Manual on another. Apply the table to the operation being worked on, not to the codebase
225
+ as a whole.
226
+
227
+ ## Gate
228
+
229
+ The backend path is decided by the backend stack, not by browser preference. Establish
230
+ this before proposing edits. Discovering a backend blocker after the browser half is
231
+ written leaves tokens flowing with nothing enforcing them.
232
+
233
+ An integration is one backend path plus one browser mode, and they are independent
234
+ choices. A non-Spring-MVC backend does not rule out Seamless mode; it only rules out the
235
+ Spring Boot 3 starter backend path.
236
+
237
+ Backend path:
238
+
239
+ | Backend path | Requirement | Reference |
240
+ | --- | --- | --- |
241
+ | **Spring Boot 3 starter** | Spring Boot 3 with Spring MVC, and Java 17+ | `backend-spring-boot.md` |
242
+ | **Java core SDK** | Java 17+ | `backend-java-core.md` |
243
+
244
+ Browser mode:
245
+
246
+ | Browser mode | Use when | Reference |
247
+ | --- | --- | --- |
248
+ | **Seamless** | The page reaches the endpoint with `fetch` or async `XMLHttpRequest` | `browser-seamless.md` |
249
+ | **Manual** | The call needs explicit token timing, or a non-header carrier | `browser-manual.md` |
250
+
251
+ Either browser mode pairs with either backend path. The token travels in
252
+ `X-IntelliFend-Token` regardless. Java below 17 blocks both backend paths, and no amount
253
+ of browser-side work substitutes for backend enforcement.
254
+
255
+ ### Run the check
256
+
257
+ Find the build file for the module that serves the protected endpoint, not the first one
258
+ in the tree. In a monorepo, locate the route first:
259
+
260
+ ```bash
261
+ find . -maxdepth 4 \( -name pom.xml -o -name "build.gradle*" \) \
262
+ -not -path "*/node_modules/*" -not -path "*/build/*" -not -path "*/target/*"
263
+ rg -n "/api/register" -g "*.java" -g "*.kt" .
264
+ ```
265
+
266
+ One build file, inspect it. Several, and the route cannot be tied to one of them — or
267
+ several backends could plausibly serve it — **stop and ask which service owns the
268
+ endpoint.** Two backends in a monorepo can differ in both Java version and framework, so
269
+ a guess here invalidates the whole integration rather than needing a tweak.
270
+
271
+ Use repo-wide build-file scans only for discovery. Do not combine a route found in one
272
+ module with Java or Spring evidence from another module.
273
+
274
+ #### Java target version
275
+
276
+ Read the *project's target*, never the machine's JDK.
277
+
278
+ ```bash
279
+ rg -n "maven\.compiler\.(release|source|target)|<release>|<java\.version>" <owning-pom.xml>
280
+ rg -n "languageVersion|sourceCompatibility|jvmToolchain|JavaVersion" <owning-build.gradle*>
281
+ ```
282
+
283
+ - **Maven** — `maven.compiler.release`, or the compiler plugin's `<release>`, is
284
+ authoritative. Spring Boot's parent POM exposes `<java.version>`, which is the
285
+ idiomatic place in a Boot app. **Check the parent POM too** when the module inherits;
286
+ the value is often not in the module's own file.
287
+ - **Gradle** — `java.toolchain.languageVersion = JavaLanguageVersion.of(21)` and
288
+ `kotlin { jvmToolchain(21) }` are both authoritative, and both easy to miss because
289
+ they sit inside a block rather than on a property line.
290
+ - **When the build file says nothing** — `.java-version`, `.sdkmanrc`, `.tool-versions`,
291
+ a `setup-java` step's `java-version` in `.github/workflows/`, or a JDK base image in a
292
+ `Dockerfile`. These are **hints, not answers**: they describe an environment, which can
293
+ differ from what the build compiles against. A build file that yields only hints is the
294
+ ambiguous row below — ask.
295
+ - `java -version` reports the **local** JDK. A repo targeting 11 builds fine on a machine
296
+ with 21 installed, so it is a last-resort hint and never evidence.
297
+
298
+ Java below 17 blocks **both** backend paths. Report the version and its source and name
299
+ the upgrade as the prerequisite; do not fall back to an older SDK version or write a
300
+ partial browser-only integration.
301
+
302
+ #### Spring Boot 3, and separately Spring MVC
303
+
304
+ Two conditions. Both must hold for the Spring Boot starter.
305
+
306
+ ```bash
307
+ rg -n "spring-boot-starter-parent|spring-boot-dependencies|org\.springframework\.boot" \
308
+ <owning-pom.xml-or-build.gradle*> | rg "3\.[0-9]+"
309
+ rg -n "spring-boot-starter-web[\"' :<]|spring-boot-starter-webflux|spring-webmvc" \
310
+ <owning-pom.xml-or-build.gradle*>
311
+ ```
312
+
313
+ Read the parent version, the `spring-boot-dependencies` BOM version, or the Gradle plugin
314
+ version. **Spring Boot 2.x does not qualify** — the starter is a Boot 3 artifact, so a
315
+ 2.x app needs the core SDK path even though it is Spring.
316
+
317
+ Then MVC versus WebFlux — the trap worth checking every time:
318
+
319
+ - `spring-boot-starter-web` → Spring MVC. The starter is available when the app is also
320
+ Spring Boot 3 on Java 17+.
321
+ - `spring-boot-starter-webflux` with no `-web` → **not** Spring MVC. The starter protects
322
+ Spring MVC handler methods, so `@RequestShieldProtected` does not apply. Such an app is
323
+ Spring Boot 3 on Java 17+ and still needs the Java core SDK — say so explicitly,
324
+ because "Spring Boot 3, Java 21" reads like a starter green light and quietly is not.
325
+ Browser mode is unaffected; Seamless remains available when the request transport
326
+ matches.
327
+ - **Both present** → determine which stack serves the protected route. A
328
+ `@RestController` returning `Mono`/`Flux`, or a `RouterFunction`, indicates the
329
+ reactive path. Ask if it stays unclear.
330
+
331
+ Also not Spring MVC, and therefore the Java core SDK backend path: **Quarkus, Micronaut,
332
+ Dropwizard, Helidon, Ktor, Vert.x, Jakarta EE / JAX-RS on its own, and a plain servlet
333
+ application.**
334
+
335
+ ## Then act on what you found
336
+
337
+ This decision chooses the backend path only. Pick the browser mode separately from how
338
+ the page issues the protected request.
339
+
340
+ | Detected | Do this |
341
+ | --- | --- |
342
+ | Java < 17, or no Java backend | Stop before editing. Report the finding and its source; no backend path is available, so nothing can enforce. Name the upgrade as the prerequisite. |
343
+ | Java 17+, no Spring Boot 3 MVC | Use Java core SDK. It is the only backend option; state it rather than asking a fake choice. |
344
+ | Java 17+ and Spring Boot 3 MVC | Both backend paths are viable. Recommend the starter and confirm once. |
345
+ | Ambiguous module, missing build file, version only in environment hints, or unclear MVC/WebFlux ownership | Ask, showing what you found. |
346
+
347
+ Always report the evidence with file and line references:
348
+
349
+ ```text
350
+ Backend: theair-customer-backend/build.gradle.kts
351
+ Java 21 - java.toolchain.languageVersion (line 14)
352
+ Spring Boot 3.2.1 - org.springframework.boot plugin (line 3)
353
+ Spring MVC - spring-boot-starter-web (line 20)
354
+ Backend path: Spring Boot 3 starter. Browser mode still depends on released modes and request transport.
355
+ ```
356
+
357
+ Detection is inference from build files. If any line would read "unknown", ask rather
358
+ than assume.
359
+
360
+ ## Choosing the mode
361
+
362
+ Reach this section only when **Contract check** left a genuine choice: both modes
363
+ released, and this operation not already integrated. If it landed on any other row of
364
+ that table, the mode is already decided — do not re-open it here.
365
+
366
+ This is the browser half. Backend path does not constrain it. Both browser modes deliver
367
+ the token in `X-IntelliFend-Token`, which either backend path reads.
368
+
369
+ | | **Seamless** | **Manual** |
370
+ | --- | --- | --- |
371
+ | Browser | `data-protect` lists exact endpoints; the SDK attaches tokens to matching `fetch` and async XHR requests. | `await IntelliFend.getToken()` at each protected call site, attached in application code. |
372
+ | Requires | The request travels by `fetch` or async XHR from page scope. | Nothing beyond the SDK and a supported carrier. |
373
+ | Code touched | One line of HTML. | Every protected call site, plus every retry path. |
374
+ | Backend work | Identical either way: whichever backend path the Gate selected. | Identical either way: whichever backend path the Gate selected. |
375
+
376
+ Released-mode availability was already settled at **Contract check**. One question is
377
+ left: does the protected request travel by `fetch` or asynchronous `XMLHttpRequest` from
378
+ page scope? Native form POST, `sendBeacon`, WebSocket/EventSource, synchronous XHR,
379
+ `no-cors`, and service-worker-owned requests are not intercepted — read
380
+ `browser-seamless.md` for the full coverage list.
381
+
382
+ Recommend Seamless when that check passes. It touches one line instead of every call
383
+ site, so there is no missed call site or retry path. If it fails, use Manual and give
384
+ the reason; that is a finding, not a preference.
385
+
386
+ A given protected operation uses one mode, not both. Different endpoints on the same
387
+ page may use different modes when necessary. Never use two backend verification paths on
388
+ one request; `@RequestShieldProtected` and manual `RequestShieldClient.verify()` both
389
+ consume the token, so the second returns `token_replayed`.