{"record":{"id":"7ff32edfcd0f0d60","repo":"KeygraphHQ/shannon","slug":"login-instructions-template-not-found","errorCode":null,"errorMessage":"Login instructions template not found","messagePattern":"Login instructions template not found","errorType":"exception","errorClass":"PentestError","httpStatus":null,"severity":"error","filePath":"apps/worker/src/services/prompt-manager.ts","lineNumber":163,"sourceCode":"}\n\ninterface IncludeReplacement {\n  placeholder: string;\n  content: string;\n}\n\n// Pure function: Build complete login instructions from config\nasync function buildLoginInstructions(\n  authentication: Authentication,\n  logger: ActivityLogger,\n  promptsBaseDir: string = PROMPTS_DIR,\n): Promise<string> {\n  try {\n    // 1. Load the login instructions template\n    const loginInstructionsPath = path.join(promptsBaseDir, 'shared', 'login-instructions.txt');\n\n    if (!(await fs.pathExists(loginInstructionsPath))) {\n      throw new PentestError('Login instructions template not found', 'filesystem', false, { loginInstructionsPath });\n    }\n\n    const fullTemplate = await fs.readFile(loginInstructionsPath, 'utf8');\n\n    const getSection = (content: string, sectionName: string): string => {\n      const regex = new RegExp(`<!-- BEGIN:${sectionName} -->([\\\\s\\\\S]*?)<!-- END:${sectionName} -->`, 'g');\n      const match = regex.exec(content);\n      return match?.[1]?.trim() ?? '';\n    };\n\n    // 2. Extract sections based on login type\n    const loginType = authentication.login_type?.toUpperCase();\n    let loginInstructions = '';\n\n    const commonSection = getSection(fullTemplate, 'COMMON');\n    const authSection = loginType ? getSection(fullTemplate, loginType) : ''; // FORM or SSO\n    const verificationSection = getSection(fullTemplate, 'VERIFICATION');\n","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/KeygraphHQ/shannon/blob/1ae0a142f8525410a688f0309fd003cc5b1d92de/apps/worker/src/services/prompt-manager.ts#L145-L181","documentation":"Thrown by buildLoginInstructions when the shared login template file at <promptsBaseDir>/shared/login-instructions.txt does not exist (fs.pathExists returns false). The template is required whenever config.authentication is set, because interpolateVariables calls buildLoginInstructions to fill the {{LOGIN_INSTRUCTIONS}} placeholder. Category 'filesystem', non-retryable.","triggerScenarios":"A scan config declares an authentication block (config.authentication.login_flow or login_type) so interpolateVariables invokes buildLoginInstructions, but the prompts directory mounted into the worker container is missing shared/login-instructions.txt. Common in local mode when the prompts dir override (SHANNON_WORKER_ROOT or promptDir) points at a partial tree, or in a custom Docker image that did not copy apps/worker/prompts/.","commonSituations":"Running local mode with a promptDir override that omits the shared/ subfolder. A custom worker image that pruned prompt files. An npx image older than the code expecting a newer template. Mis-set SHANNON_WORKER_ROOT pointing at the repo root instead of the worker package.","solutions":["Verify the file exists in the running container: docker exec <worker> ls /app/apps/worker/prompts/shared/login-instructions.txt (npx) or check ./apps/worker/prompts/shared/login-instructions.txt (local).","Restore or rebuild the prompts tree from the repo, then re-run.","If using a promptDir override, confirm it resolves to a directory that still contains shared/login-instructions.txt.","In local mode, ensure SHANNON_WORKER_ROOT is unset or correctly points at apps/worker so PROMPTS_DIR resolves to the full prompt set."],"exampleFix":"// before: promptDir override omits shared/\n//   loadPrompt('vuln-injection', vars, config, false, logger, './my-prompts')\n//   -> buildLoginInstructions looks for ./my-prompts/shared/login-instructions.txt (missing)\n// after: keep the standard prompts dir, or symlink the shared tree\n//   ln -s ./apps/worker/prompts/shared ./my-prompts/shared","handlingStrategy":"validation","validationCode":"// Before starting an authenticated scan, confirm the template is present and readable\nimport { pathExists } from 'fs-extra';\nconst templatePath = path.join(PROMPTS_DIR, 'shared', 'login-instructions.txt');\nif (config.authentication && !(await pathExists(templatePath))) {\n  throw new Error(`Missing required login template at ${templatePath}`);\n}","typeGuard":"function hasAuthentication(auth: unknown): auth is Authentication {\n  return typeof auth === 'object' && auth !== null &&\n    ('login_type' in auth || 'login_flow' in auth || 'credentials' in auth);\n}","tryCatchPattern":"try {\n  await loadPrompt('vuln-auth', vars, config, false, logger);\n} catch (e) {\n  if (e instanceof PentestError && e.type === 'filesystem' && /Login instructions template not found/.test(e.message)) {\n    // restore the prompts tree in the image, then retry; otherwise disable auth and re-run unauthenticated\n  }\n  throw e;\n}","preventionTips":["Build the worker image from a clean checkout so apps/worker/prompts/shared/ is always included.","When overriding promptDir, point it at the full prompts tree, not a subset missing shared/.","In local mode, leave SHANNON_WORKER_ROOT unset unless you know the prompts path resolution.","Add a preflight check that asserts shared/login-instructions.txt exists when config.authentication is set."],"tags":["prompt","authentication","filesystem","config","template"],"backgroundTag":null,"analyzedSha":"1ae0a142f8525410a688f0309fd003cc5b1d92de","analyzedAt":"2026-08-12T17:40:03.583Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}