@lunora/advisor 1.0.0-alpha.8 → 1.0.0-alpha.80
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/LICENSE.md +6 -0
- package/README.md +60 -5
- package/dist/index.d.mts +4033 -1072
- package/dist/index.d.ts +4033 -1072
- package/dist/index.mjs +1 -85
- package/dist/packem_shared/MAP_VERSION-DEJGm0wI.mjs +1 -0
- package/dist/packem_shared/actionFetchSsrf-Z61P0o8U.mjs +1 -0
- package/dist/packem_shared/actionWithoutErrorHandling-4GCBT0_z.mjs +1 -0
- package/dist/packem_shared/adminRouteWithoutGuard-DPE7LuNh.mjs +1 -0
- package/dist/packem_shared/aiRawRunEscapeHatch-Dq43DVD9.mjs +1 -0
- package/dist/packem_shared/aiRunWithoutLogging-CvHHtEN9.mjs +1 -0
- package/dist/packem_shared/aiToolSideEffectPromptInjection-K42X3QzJ.mjs +1 -0
- package/dist/packem_shared/aiUnboundedGenerationPublic-C17h6sMV.mjs +1 -0
- package/dist/packem_shared/allowUnauthenticatedShardAccessEnabled-CjXI-kda.mjs +1 -0
- package/dist/packem_shared/argument-derived-sink-DLXFC79t.mjs +1 -0
- package/dist/packem_shared/authApiCallWithoutHeaders-C1OOWML5.mjs +1 -0
- package/dist/packem_shared/authCsrfCheckDisabled-Dz27bDzp.mjs +1 -0
- package/dist/packem_shared/authEmailVerificationDisabled-QTDD7TAU.mjs +1 -0
- package/dist/packem_shared/authScimWithoutTransactions-FqdTmUJs.mjs +1 -0
- package/dist/packem_shared/authSecureCookiesDisabled-CrGYulfJ.mjs +1 -0
- package/dist/packem_shared/authSessionFreshageZero-yWE6CGYP.mjs +1 -0
- package/dist/packem_shared/authTrustedOriginsWildcard-xpeRXfGF.mjs +1 -0
- package/dist/packem_shared/browserAllowPrivateTargets-Cj5sizhv.mjs +1 -0
- package/dist/packem_shared/browserUserUrlWithoutAllowlist-Wl3xHr7v.mjs +1 -0
- package/dist/packem_shared/circularFk-DtcWFJxK.mjs +1 -0
- package/dist/packem_shared/classifySensitivity-JnjaTGYi.mjs +1 -0
- package/dist/packem_shared/compareToBaseline-DBgN5YqX.mjs +1 -0
- package/dist/packem_shared/constraintValidator-DoRJD9Is.mjs +1 -0
- package/dist/packem_shared/containerInstanceKeyFromUserInput-uEQQVsEz.mjs +1 -0
- package/dist/packem_shared/containerOversizedInstance-Bx89uR7E.mjs +1 -0
- package/dist/packem_shared/containerPublicInternet-BFfZf_P4.mjs +1 -0
- package/dist/packem_shared/containerRuntimeEgressRelaxation-pkpyXNou.mjs +1 -0
- package/dist/packem_shared/containerStartEnableInternetOverride-BBY4PMG1.mjs +1 -0
- package/dist/packem_shared/dedupeCacheKeys-DtBOHffV.mjs +1 -0
- package/dist/packem_shared/duplicateIndex-Cip6-Rpu.mjs +1 -0
- package/dist/packem_shared/emptyIndex-BnHDcXza.mjs +1 -0
- package/dist/packem_shared/errorRateOutlier-pr3_ZKbq.mjs +1 -0
- package/dist/packem_shared/errorWithoutCatalog-BTfvaXHR.mjs +1 -0
- package/dist/packem_shared/exportSinkMisconfigured-JfbAx9AI.mjs +1 -0
- package/dist/packem_shared/externalSourceIncrementalNoDeletePath-BCzm3HzF.mjs +1 -0
- package/dist/packem_shared/externalSourceOnGlobal-CH7xbJ49.mjs +1 -0
- package/dist/packem_shared/externalSourceUnscoped-BxU2uSXk.mjs +1 -0
- package/dist/packem_shared/fanOutBreadth-CBtmZnoh.mjs +1 -0
- package/dist/packem_shared/filterOnPrimaryKey-c37rjqBk.mjs +1 -0
- package/dist/packem_shared/filterWithoutIndex-DXb__LGI.mjs +1 -0
- package/dist/packem_shared/finding-NrKO8idM.mjs +1 -0
- package/dist/packem_shared/fk-index-DXYS17cc.mjs +1 -0
- package/dist/packem_shared/flagGatesSecurityWithUnsafeDefault-i-Befg8b.mjs +1 -0
- package/dist/packem_shared/fromServerSchema-w16riDj2.mjs +1 -0
- package/dist/packem_shared/geoIndexFieldNotGeopoint-D0lOTm-_.mjs +1 -0
- package/dist/packem_shared/geoIndexUnused-D7C9Qr4U.mjs +1 -0
- package/dist/packem_shared/globalTableNearColumnLimit-BFbBBd6A.mjs +1 -0
- package/dist/packem_shared/gradeFromScore-KSt58rj1.mjs +1 -0
- package/dist/packem_shared/hardcodedSecret-Bw_4FYrs.mjs +1 -0
- package/dist/packem_shared/helpers-DcWLLrH0.mjs +1 -0
- package/dist/packem_shared/hotShard-BwGYZ3Tq.mjs +1 -0
- package/dist/packem_shared/httpActionMissingAuthGuard-BS3JZgaz.mjs +1 -0
- package/dist/packem_shared/httpActionResponseHeaderInjection-DHnc8c9f.mjs +1 -0
- package/dist/packem_shared/hyperdriveOutsideAction-CPDdGP2g.mjs +1 -0
- package/dist/packem_shared/identityUndeclaredClaimTrusted-BDFqB7Dw.mjs +1 -0
- package/dist/packem_shared/imagesUrlSourceFromUserInput-DizsRh3M.mjs +1 -0
- package/dist/packem_shared/indexReferencesUnknownField-Dxd5jcVh.mjs +1 -0
- package/dist/packem_shared/indexUtilization-CkVPZcVe.mjs +1 -0
- package/dist/packem_shared/insertManyUnsafeUserData-DWE8d_DF.mjs +1 -0
- package/dist/packem_shared/kvUnscopedUserKeyIdor-CIgMPzFj.mjs +1 -0
- package/dist/packem_shared/mailInboundDispatchWithoutVerify-CWwqXyPX.mjs +1 -0
- package/dist/packem_shared/mailRecipientFromRequestInput-SDCchLP7.mjs +1 -0
- package/dist/packem_shared/maskUncoveredPiiColumn-axEka4nd.mjs +1 -0
- package/dist/packem_shared/maskWeakHashStrategyOnPii-d9BGImzK.mjs +1 -0
- package/dist/packem_shared/maskedRelationLeakViaWith-C8n-Mzb1.mjs +1 -0
- package/dist/packem_shared/mutatorFullRowReplace-BzRpZ47r.mjs +1 -0
- package/dist/packem_shared/nondeterministicQueryMutation-sxmixc0l.mjs +1 -0
- package/dist/packem_shared/normalizeIdUsedAsAuthorization-BXN-Can6.mjs +1 -0
- package/dist/packem_shared/notifyMissingPushConfig-DeQMoNwm.mjs +1 -0
- package/dist/packem_shared/notifySendOutsideAction-CU4T16cR.mjs +1 -0
- package/dist/packem_shared/outputProjectionMissingOnPublicRead-BN5-uW0p.mjs +1 -0
- package/dist/packem_shared/ownerFieldFromArgsNotAuth-CjOSUDKi.mjs +1 -0
- package/dist/packem_shared/paymentCreateWithoutAuthorize-CICGmRFR.mjs +1 -0
- package/dist/packem_shared/paymentWebhookWideTolerance-B-F5jOeA.mjs +1 -0
- package/dist/packem_shared/plaintextSecretInWranglerVariables-bNkqmNqV.mjs +1 -0
- package/dist/packem_shared/policyReferencesUnknownTable-CIKuRZ5Y.mjs +1 -0
- package/dist/packem_shared/privilegedDispatchUnvalidatedPayload-C6Qtc8aT.mjs +1 -0
- package/dist/packem_shared/privilegedFanoutFromPublicProcedure-Coqt23CQ.mjs +1 -0
- package/dist/packem_shared/procedure-protections-DRQotu9I.mjs +1 -0
- package/dist/packem_shared/procedureWithoutStructuredEvent-CZ_B23nM.mjs +1 -0
- package/dist/packem_shared/publicArgumentUsesAny-BDEvMA8Q.mjs +1 -0
- package/dist/packem_shared/publicMutationWithoutRatelimit-DbczxgpL.mjs +1 -0
- package/dist/packem_shared/publicTableRlsOptoutConfusion-C9wCtAfg.mjs +1 -0
- package/dist/packem_shared/queueWithoutDlq-BvHz3Opg.mjs +1 -0
- package/dist/packem_shared/r2sqlOutsideAction-BdLiLOAX.mjs +1 -0
- package/dist/packem_shared/ratelimitDefaultMemoryStore-Dj0Kk7t9.mjs +1 -0
- package/dist/packem_shared/ratelimitKeySpoofableOrGlobal-BzDzxnXm.mjs +1 -0
- package/dist/packem_shared/ratelimitMiddlewareFailOpen-iDJq68qx.mjs +1 -0
- package/dist/packem_shared/relationReferencesUnknownField-BmWWEFsB.mjs +1 -0
- package/dist/packem_shared/relationReferencesUnknownTable-CP4aWtAJ.mjs +1 -0
- package/dist/packem_shared/rlsUncoveredTable-CZ4ie4gX.mjs +1 -0
- package/dist/packem_shared/shapeTargetsGlobalTable-Bu3eEDic.mjs +1 -0
- package/dist/packem_shared/shapeUnknownTable-CREfNnWi.mjs +1 -0
- package/dist/packem_shared/signupMutationWithoutDisposableGating-D9MFN8QY.mjs +1 -0
- package/dist/packem_shared/softDeleteIncludeDeletedFromArgs-C7Ugg4zb.mjs +1 -0
- package/dist/packem_shared/sqlInjectionRisk-CslaRBxz.mjs +1 -0
- package/dist/packem_shared/storageGenerateUploadUrlNoContentTypePin-CTIU_7Fj.mjs +1 -0
- package/dist/packem_shared/storageKeyFromUserArgs-C-QUcdJ3.mjs +1 -0
- package/dist/packem_shared/storagePresignedUrlForPrivateContent-yeu1s81k.mjs +1 -0
- package/dist/packem_shared/storageUploadWithoutContentTypeAllowlist-KSRG81Iu.mjs +1 -0
- package/dist/packem_shared/storageUploadWithoutMaxSize-BTiM9Q3A.mjs +1 -0
- package/dist/packem_shared/tableWithoutInsert-DQ-GxjFF.mjs +1 -0
- package/dist/packem_shared/ttlFieldNotTimestamp-E14I83wY.mjs +1 -0
- package/dist/packem_shared/unboundedStringArgument-DzbFJc5q.mjs +1 -0
- package/dist/packem_shared/unindexedForeignKey-Dypgn8uH.mjs +1 -0
- package/dist/packem_shared/unindexedRelationTarget-CSqRJWYZ.mjs +1 -0
- package/dist/packem_shared/unrestrictedWhereBranch-CcIBmHik.mjs +1 -0
- package/dist/packem_shared/userCreatingMutationWithoutCaptcha-lGWw1o2a.mjs +1 -0
- package/dist/packem_shared/vectorsNamespaceFromUserInput-CDSOrkyX.mjs +1 -0
- package/dist/packem_shared/workflowDuplicateStepName-BU4rg5So.mjs +1 -0
- package/dist/packem_shared/workflowUnknownTarget-B8H7jwnH.mjs +1 -0
- package/dist/packem_shared/workflowUnused-BUSOPdHq.mjs +1 -0
- package/package.json +3 -2
- package/dist/packem_shared/AE_METRIC_EVENTS-DexctYv6.mjs +0 -85
- package/dist/packem_shared/adminRouteWithoutGuard-UUGBkAjU.mjs +0 -33
- package/dist/packem_shared/authApiCallWithoutHeaders-BeJhCZaf.mjs +0 -38
- package/dist/packem_shared/circularFk-B2freHrP.mjs +0 -84
- package/dist/packem_shared/constraintValidator-Dr9Py3FD.mjs +0 -186
- package/dist/packem_shared/containerOversizedInstance-5U1VKPRM.mjs +0 -36
- package/dist/packem_shared/containerPublicInternet-CuNerJE5.mjs +0 -30
- package/dist/packem_shared/duplicateIndex-BOublMSt.mjs +0 -57
- package/dist/packem_shared/emptyIndex-BX8EuEY7.mjs +0 -32
- package/dist/packem_shared/filterWithoutIndex-BYVeJaSs.mjs +0 -31
- package/dist/packem_shared/finding-Dm_zvzS1.mjs +0 -16
- package/dist/packem_shared/fk-index-IUK1ukgs.mjs +0 -7
- package/dist/packem_shared/fromServerSchema-TWb7dTHC.mjs +0 -51
- package/dist/packem_shared/hardcodedSecret-W2pz1UZB.mjs +0 -35
- package/dist/packem_shared/helpers-DNCkMWZQ.mjs +0 -4
- package/dist/packem_shared/hotShard-Ir5D0B6J.mjs +0 -48
- package/dist/packem_shared/hyperdriveOutsideAction-BgZqX7Xg.mjs +0 -30
- package/dist/packem_shared/indexReferencesUnknownField-DH0_dbUY.mjs +0 -36
- package/dist/packem_shared/indexUtilization-B5DMQ3bI.mjs +0 -45
- package/dist/packem_shared/maskUncoveredPiiColumn-DjGIPG6M.mjs +0 -61
- package/dist/packem_shared/mutatorFullRowReplace-BJnNDaIV.mjs +0 -26
- package/dist/packem_shared/nondeterministicQueryMutation-GXES1fLp.mjs +0 -35
- package/dist/packem_shared/policyReferencesUnknownTable-DtaIEovd.mjs +0 -38
- package/dist/packem_shared/publicArgumentUsesAny-C71b2NCf.mjs +0 -32
- package/dist/packem_shared/publicMutationWithoutRatelimit-xBpJ6GWK.mjs +0 -36
- package/dist/packem_shared/r2sqlOutsideAction-CtqxvMuV.mjs +0 -30
- package/dist/packem_shared/relationReferencesUnknownField-YznyXt_7.mjs +0 -54
- package/dist/packem_shared/relationReferencesUnknownTable-DrorpKYe.mjs +0 -33
- package/dist/packem_shared/rlsUncoveredTable-CxEfZ5eZ.mjs +0 -56
- package/dist/packem_shared/shapeTargetsGlobalTable-DHrf4Koi.mjs +0 -34
- package/dist/packem_shared/shapeUnknownTable-C8aDWFoe.mjs +0 -34
- package/dist/packem_shared/sqlInjectionRisk-zwytYGLt.mjs +0 -26
- package/dist/packem_shared/tableWithoutInsert-CbbaYIP4.mjs +0 -34
- package/dist/packem_shared/unboundedStringArgument-DThg2-wt.mjs +0 -32
- package/dist/packem_shared/unindexedForeignKey-BgJbKyqK.mjs +0 -45
- package/dist/packem_shared/unindexedRelationTarget-D6eyj6Xx.mjs +0 -53
- package/dist/packem_shared/userCreatingMutationWithoutCaptcha-CH31YsUZ.mjs +0 -42
- package/dist/packem_shared/workflowUnknownTarget-Cdd7WhKQ.mjs +0 -34
- package/dist/packem_shared/workflowUnused-D0jHxdz9.mjs +0 -38
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { e as emit } from './finding-Dm_zvzS1.mjs';
|
|
2
|
-
import { P as PRIMARY_KEY, l as leadingIndexedColumns, s as suggestIndexName } from './fk-index-IUK1ukgs.mjs';
|
|
3
|
-
|
|
4
|
-
const unindexedRelationTarget = {
|
|
5
|
-
categories: ["PERFORMANCE"],
|
|
6
|
-
description: "A `many` relation's foreign-key column on the target table has no index leading with it. Relation predicates (`some`/`none`/`every`) and `with:` child reads filter the target on that column, full-scanning it as rows accumulate.",
|
|
7
|
-
facing: "EXTERNAL",
|
|
8
|
-
level: "INFO",
|
|
9
|
-
name: "unindexed_relation_target",
|
|
10
|
-
remediation: 'Add a secondary index on the target table leading with the FK column, e.g. `.index("byAuthorId", ["authorId"])`.',
|
|
11
|
-
run: (context) => {
|
|
12
|
-
const findings = [];
|
|
13
|
-
const tablesByName = new Map(context.schema.tables.map((table) => [table.name, table]));
|
|
14
|
-
for (const table of context.schema.tables) {
|
|
15
|
-
for (const relation of table.relations) {
|
|
16
|
-
if (relation.kind !== "many") {
|
|
17
|
-
continue;
|
|
18
|
-
}
|
|
19
|
-
const target = tablesByName.get(relation.table);
|
|
20
|
-
if (target === void 0) {
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
const fkColumn = relation.field;
|
|
24
|
-
if (fkColumn === PRIMARY_KEY) {
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
const coveredByForeignKeyLint = target.relations.some((targetRelation) => targetRelation.kind === "one" && targetRelation.field === fkColumn);
|
|
28
|
-
if (coveredByForeignKeyLint || leadingIndexedColumns(target).has(fkColumn)) {
|
|
29
|
-
continue;
|
|
30
|
-
}
|
|
31
|
-
const suggestedIndex = suggestIndexName(fkColumn);
|
|
32
|
-
findings.push(
|
|
33
|
-
emit(unindexedRelationTarget, {
|
|
34
|
-
cacheKey: `unindexed_relation_target:${relation.table}:${fkColumn}`,
|
|
35
|
-
detail: `Relation "${relation.name}" on table "${table.name}" is a to-many over "${relation.table}" via its column "${fkColumn}", which is not the leading column of any index on "${relation.table}". Relation predicates (\`some\`/\`none\`/\`every\`) and \`with:\` reads of "${relation.name}" full-scan "${relation.table}".`,
|
|
36
|
-
metadata: {
|
|
37
|
-
fkColumn,
|
|
38
|
-
references: { column: relation.references, table: table.name },
|
|
39
|
-
relation: relation.name,
|
|
40
|
-
suggestedIndex: { fields: [fkColumn], name: suggestedIndex },
|
|
41
|
-
table: relation.table
|
|
42
|
-
}
|
|
43
|
-
})
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return findings;
|
|
48
|
-
},
|
|
49
|
-
source: "static",
|
|
50
|
-
title: "Unindexed relation target"
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export { unindexedRelationTarget as default };
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { e as emit } from './finding-Dm_zvzS1.mjs';
|
|
2
|
-
|
|
3
|
-
const userCreatingMutationWithoutCaptcha = {
|
|
4
|
-
categories: ["SECURITY"],
|
|
5
|
-
description: "A public `mutation`/`action` that creates a user/session or sends mail has no CAPTCHA / bot check. Account-creating and mail-sending endpoints are prime automated-abuse targets (credential stuffing, mailbox flooding, disposable-account farming).",
|
|
6
|
-
facing: "EXTERNAL",
|
|
7
|
-
level: "WARN",
|
|
8
|
-
name: "user_creating_mutation_without_captcha",
|
|
9
|
-
remediation: "Add a server-verified human check: `.use(verifyTurnstile({ secret, token }))` from `@lunora/auth`, or wrap it with `.use(protectPublic({ rateLimit, captcha }))` from `@lunora/server`. Pair with a rate limit for defense in depth.",
|
|
10
|
-
run: (context) => {
|
|
11
|
-
if (context.procedureProtections === void 0) {
|
|
12
|
-
return [];
|
|
13
|
-
}
|
|
14
|
-
const findings = [];
|
|
15
|
-
for (const procedure of context.procedureProtections) {
|
|
16
|
-
const isPublicWrite = procedure.visibility === "public" && (procedure.kind === "mutation" || procedure.kind === "action");
|
|
17
|
-
const sensitive = procedure.writesUserTable || procedure.callsMail;
|
|
18
|
-
if (!isPublicWrite || !sensitive || procedure.usesCaptcha) {
|
|
19
|
-
continue;
|
|
20
|
-
}
|
|
21
|
-
const reason = procedure.writesUserTable ? "writes a user/session table" : "sends mail";
|
|
22
|
-
findings.push(
|
|
23
|
-
emit(userCreatingMutationWithoutCaptcha, {
|
|
24
|
-
cacheKey: `user_creating_mutation_without_captcha:${procedure.file}:${procedure.exportName}`,
|
|
25
|
-
detail: `Public ${procedure.kind} \`${procedure.exportName}\` (${procedure.file}) ${reason} but has no CAPTCHA check. Add \`.use(verifyTurnstile(...))\` or \`.use(protectPublic({ captcha }))\`.`,
|
|
26
|
-
metadata: {
|
|
27
|
-
callsMail: procedure.callsMail,
|
|
28
|
-
exportName: procedure.exportName,
|
|
29
|
-
file: procedure.file,
|
|
30
|
-
kind: procedure.kind,
|
|
31
|
-
writesUserTable: procedure.writesUserTable
|
|
32
|
-
}
|
|
33
|
-
})
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
return findings;
|
|
37
|
-
},
|
|
38
|
-
source: "static",
|
|
39
|
-
title: "Account-creating / mail-sending write without a CAPTCHA"
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export { userCreatingMutationWithoutCaptcha as default };
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { e as emit } from './finding-Dm_zvzS1.mjs';
|
|
2
|
-
|
|
3
|
-
const workflowUnknownTarget = {
|
|
4
|
-
categories: ["SCHEMA"],
|
|
5
|
-
description: 'A `ctx.workflows.get("<name>")` call references a workflow that is not declared by any `defineWorkflow` export in `lunora/workflows.ts`. The name is a typo or points at a removed/renamed workflow — the call throws at runtime.',
|
|
6
|
-
facing: "INTERNAL",
|
|
7
|
-
level: "ERROR",
|
|
8
|
-
name: "workflow_unknown_target",
|
|
9
|
-
remediation: 'Fix the workflow name in the `ctx.workflows.get("…")` call, or add the missing `defineWorkflow` export to `lunora/workflows.ts`.',
|
|
10
|
-
run: (context) => {
|
|
11
|
-
if (context.workflows === void 0 || context.workflowCalls === void 0) {
|
|
12
|
-
return [];
|
|
13
|
-
}
|
|
14
|
-
const declared = new Set(context.workflows.map((workflow) => workflow.exportName));
|
|
15
|
-
const findings = [];
|
|
16
|
-
for (const call of context.workflowCalls) {
|
|
17
|
-
if (call.workflow === "" || declared.has(call.workflow)) {
|
|
18
|
-
continue;
|
|
19
|
-
}
|
|
20
|
-
findings.push(
|
|
21
|
-
emit(workflowUnknownTarget, {
|
|
22
|
-
cacheKey: `workflow_unknown_target:${call.file}:${call.exportName}:${call.workflow}`,
|
|
23
|
-
detail: `\`ctx.workflows.get("${call.workflow}")\` in "${call.exportName}" (${call.file}) references workflow "${call.workflow}", which is not declared in lunora/workflows.ts.`,
|
|
24
|
-
metadata: { exportName: call.exportName, file: call.file, line: call.line, workflow: call.workflow }
|
|
25
|
-
})
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
return findings;
|
|
29
|
-
},
|
|
30
|
-
source: "static",
|
|
31
|
-
title: "Workflow call references unknown workflow"
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export { workflowUnknownTarget as default };
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { e as emit } from './finding-Dm_zvzS1.mjs';
|
|
2
|
-
|
|
3
|
-
const workflowUnused = {
|
|
4
|
-
categories: ["SCHEMA"],
|
|
5
|
-
description: 'No function starts this workflow via `ctx.workflows.get("<name>")`. It may be triggered through a path the advisor can\'t see (the Cloudflare API, a wrangler invocation, a cross-service binding) — or it may be dead code that still deploys as a billable WorkflowEntrypoint.',
|
|
6
|
-
facing: "INTERNAL",
|
|
7
|
-
level: "INFO",
|
|
8
|
-
name: "workflow_unused",
|
|
9
|
-
remediation: 'If the workflow should be triggered in-app, start it from a mutation/action with `ctx.workflows.get("<name>").create({ params })`. If it is started externally or is no longer needed, this advisory can be ignored (or remove the `defineWorkflow` export).',
|
|
10
|
-
run: (context) => {
|
|
11
|
-
if (context.workflows === void 0) {
|
|
12
|
-
return [];
|
|
13
|
-
}
|
|
14
|
-
const calls = context.workflowCalls ?? [];
|
|
15
|
-
if (calls.some((call) => call.workflow === "")) {
|
|
16
|
-
return [];
|
|
17
|
-
}
|
|
18
|
-
const started = new Set(calls.map((call) => call.workflow));
|
|
19
|
-
const findings = [];
|
|
20
|
-
for (const workflow of context.workflows) {
|
|
21
|
-
if (started.has(workflow.exportName)) {
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
findings.push(
|
|
25
|
-
emit(workflowUnused, {
|
|
26
|
-
cacheKey: `workflow_unused:${workflow.exportName}`,
|
|
27
|
-
detail: `No function calls \`ctx.workflows.get("${workflow.exportName}")\` — workflow "${workflow.exportName}" is declared but never started in app code.`,
|
|
28
|
-
metadata: { workflow: workflow.exportName }
|
|
29
|
-
})
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
return findings;
|
|
33
|
-
},
|
|
34
|
-
source: "static",
|
|
35
|
-
title: "Workflow is never started"
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
export { workflowUnused as default };
|