ποΈ All skills (Claude Code proprietary format, stored as
SKILL.mdfiles and invoked as slash commands) are stored into this folder.
π§βπ» This folder contains coding assistant skills that I use to perform a secure code review.
π¬ The idea is to:
- Learn how to leverage AI, via a coding assistant (claude code here), in the context of a secure code review activity.
- Define a process to perform a secure code review with the help of AI via a coding assistant.
- Allow me to learn how to create instructions for a coding assistant to help me during a secure code review activity.
sequenceDiagram
actor Me as Dom
participant R1 as code-snippets-security-utils
participant R2 as code-assistant-skills-security-utils
participant R3 as toolbox-ai-assisted-secure-code-review
Me->>Me: Find a weakness during<br/>a secure code review.
alt Remediation not documented<br/>into the repository
Me->>R1: Search, test, and create a new code snippet<br/>with a remediation proposal.
R1->>Me: Test and publishing OK.
end
alt Skill do not exist<br/>into the repository<br/>"code-assistant-skills-security-utils"
Me->>R2: Use the test work done to create the corresponding skill<br/>to generate code that prevents the weakness by default.
R2->>Me: Test and publishing OK.
end
alt New skill created into the repository<br/>"code-assistant-skills-security-utils"
Me->>R3: Look to create, if relevant, a new dedicated agent using<br/>the content of the new skill created<br/>to detect the corresponding weakness.
R3->>Me: Test and publishing OK.
end
alt Weakness not detected by the skill "codebase-hotspotsv2"<br/>so manually identified
Me->>R3: Update the skill to detect the weakness.
R3->>Me: Test and publishing OK.
Me->>R1: Create a code snippet for the corresponding remediation.
R1->>Me: Test and publishing OK.
Me->>R2: Create a skill for the corresponding weakness.
R2->>Me: Test and publishing OK.
end
π¦ Related GitHub repositories:
π¬ I imagined the following process against a codebase using claude code sessions:
π§βπ» Intital steps into a claude code session at the root folder of the codebase:
- Start a new claude code session: Important to isolate the processing from a context perspective.
- Invoke the skill
/codebase-overviewto have a global visual overview of the risky sinks.
π§βπ» For each module of the codebase into a claude code session at the root folder of the module, apply these steps:
- Scan the code with SemGrep to identify issues using a pattern-based approach: Goal is to identify issues not linked to an entry point, like for example, a deprecated algorithm used but not called from an entry point.
- Start a new claude code session: Important to isolate the processing from a context perspective.
- Invoke the skill
/codebase-semgrep-findings-reviewto filter false positive findings from the SemGrep scan results. - Start a new claude code session: Important to isolate the processing from a context perspective.
- Invoke the skill
/codebase-hotspotsv2to identify entry point that leads to risk processing from a security perspective. - Review and manually validate the result of step 3 + step 5 with the help of the skill
/codebase-review-buddy.
- An approach module by module is used to speed up the review.
- The SemGrep scan is performed via this dedicated toolbox.
π€ In this case, the context is that I received a codebase and I want to use claude code to give me the following overview:
A visual overview of the information entry points and where the information land including the type of processing
and if such processing can be risky from a security perspective.
π¦ User prompt is stored, as a claude code skill, into the file in the folder .claude/skills/codebase-overview/ (ref).
π€ Use it via this instruction inside a claude code session: /codebase-overview [RELATIVE_PATH_TO_CODEBASE].
β The generated Mermaid code was validated using the Mermaid Live editor to check its rendering, readability, and the effectiveness of the generated diagram. The Mermaid format was chosen because it is a text-based format; it can therefore be modified after generation if necessary or sent to an LLM for additional analysis rounds.
βΉοΈ Forms legend:
- Hexagon form represents a entry point.
- Rectangle form represents a custom code landing points with a TAG to indicate the type of processing performed and colored if such processing can be risky from a security perspective.
- Circle form represents a third-party library landing points and colored if processing performed can be risky from a security perspective.
βΉοΈ Node label naming conventions is defined into the section Output rules section of the skill file.
π¬ Example of generated schema against the source code of OWASP WebGoat using the download of a zip archive of the main branch:
flowchart LR
MAIN{{"StartWebGoat#main"}} --> LIB_SB(("Spring Boot"))
HH{{"HammerHead#attack"}} --> P_SESSION["org.owasp.webgoat.container.session -- SESSION"]:::med
REG{{"RegistrationController#registration"}} --> P_USERS["org.owasp.webgoat.container.users -- USER-MGMT -> AUTHN"]:::med
REGO{{"RegistrationController#registrationOAUTH"}} --> P_USERS
RPT{{"ReportCardController#reportCard"}} --> P_USERS
MENU{{"LessonMenuService#showLeftNav"}} --> P_USERS
PROG{{"LessonProgressService#lessonOverview"}} --> P_USERS
RESTART{{"RestartLessonService#restartLesson"}} --> LIB_FLYWAY(("Flyway")):::high
ENVSVC{{"EnvironmentService#homeDirectory"}} --> P_ENV["org.owasp.webgoat.container.service -- CONFIG-EXPOSURE"]:::med
LBLDBG{{"LabelDebugService#setDebuggingStatus"}} --> P_ENV
LBL{{"LabelService#fetchLabels"}} --> P_I18N["org.owasp.webgoat.container.i18n -- METADATA/I18N"]
HINT{{"HintService#getHints"}} --> P_I18N
FS_IMP{{"FileServer#importFile"}} --> LIB_FILE(("java.io / java.nio (file I/O)")):::high
FS_GET{{"FileServer#getFiles"}} --> LIB_FILE
MAIL{{"MailboxController#sendEmail"}} --> LIB_MAIL(("Spring Mail"))
WJWT_D{{"JWTController#decode"}} --> LIB_JJWT(("io.jsonwebtoken (jjwt)")):::med
WJWT_E{{"JWTController#encode"}} --> LIB_JJWT
VERIFY{{"VerifyAccount#completed"}} --> P_AUTHBYPASS["org.owasp.webgoat.lessons.authbypass -- AUTHN"]:::med
A1{{"Assignment1#completed"}} --> P_CHAL["org.owasp.webgoat.lessons.challenges -- AUTHN -> ACCOUNT-RECOVERY"]:::med
A7R{{"Assignment7#resetPassword"}} --> P_CHAL
A8V{{"Assignment8#vote"}} --> P_CHAL
IMG{{"ImageServlet#logo"}} --> LIB_FILE
A5{{"Assignment5#login"}} --> LIB_JDBC(("java.sql / JDBC")):::high
A7L{{"Assignment7#sendPasswordResetLink"}} --> LIB_REST(("Spring RestTemplate")):::med
ENC{{"EncodingAssignment#completed"}} --> P_CRYPTO["org.owasp.webgoat.lessons.cryptography -- CRYPTO"]:::med
HMD5{{"HashingAssignment#getMd5"}} --> LIB_CRYPTO(("java.security / javax.crypto")):::med
HSHA{{"HashingAssignment#getSha256"}} --> LIB_CRYPTO
SDEF{{"SecureDefaultsAssignment#completed"}} --> LIB_CRYPTO
SIGN{{"SigningAssignment#completed"}} --> LIB_CRYPTO
SIGNK{{"SigningAssignment#getPrivateKey"}} --> LIB_CRYPTO
FR_R{{"ForgedReviews#retrieveReviews"}} --> P_CSRF["org.owasp.webgoat.lessons.csrf -- CSRF/STATE-CHANGE"]:::med
FR_C{{"ForgedReviews#createNewReview"}} --> P_CSRF
DESER{{"InsecureDeserializationTask#completed"}} --> LIB_OIS(("java.io.ObjectInputStream")):::high
HIJACK{{"HijackSessionAssignment#login"}} --> P_HIJACK["org.owasp.webgoat.lessons.hijacksession.cas -- AUTHN -> SESSION"]:::med
IDOR_V{{"IDORViewOtherProfile#completed"}} --> P_IDOR["org.owasp.webgoat.lessons.idor -- AUTHZ -> ACCESS-CONTROL"]:::med
IDOR_E{{"IDOREditOtherProfile#completed"}} --> P_IDOR
IDOR_O{{"IDORViewOwnProfile#invoke"}} --> P_IDOR
IDOR_L{{"IDORLogin#completed"}} --> P_IDOR
INSLOG{{"InsecureLoginTask#completed"}} --> P_INSLOG["org.owasp.webgoat.lessons.insecurelogin -- AUTHN"]:::med
JWT_DEC{{"JWTDecodeEndpoint#decode"}} --> P_JWT["org.owasp.webgoat.lessons.jwt -- AUTHN -> SESSION"]:::med
JWT_SK{{"JWTSecretKeyEndpoint#login"}} --> LIB_JJWT
JWT_RF{{"JWTRefreshEndpoint#newToken"}} --> LIB_JJWT
JWT_VT{{"JWTVotesEndpoint#vote"}} --> LIB_JJWT
JWT_JKU{{"JWTHeaderJKUEndpoint#resetVotes"}} --> LIB_AUTH0(("com.auth0.jwt")):::med
JWT_KID{{"JWTHeaderKIDEndpoint#resetVotes"}} --> LIB_JDBC
JWT_KID --> LIB_JJWT
LOGSP{{"LogSpoofingTask#completed"}} --> P_LOG["org.owasp.webgoat.lessons.logging -- LOG-INJECTION"]
LOGBL{{"LogBleedingTask#completed"}} --> LIB_SLF4J(("slf4j"))
MAC_L{{"MissingFunctionACUsers#listUsers"}} --> P_MAC["org.owasp.webgoat.lessons.missingac -- AUTHZ -> ACCESS-CONTROL"]:::med
MAC_S{{"MissingFunctionACUsers#usersService"}} --> LIB_JDBC
MAC_A{{"MissingFunctionACUsers#addUser"}} --> LIB_JDBC
MAC_H{{"MissingFunctionACHiddenMenus#completed"}} --> P_MAC
OR1{{"OpenRedirectTask1#simulate"}} --> P_REDIR["org.owasp.webgoat.lessons.openredirect -- REDIRECT"]:::med
OR4{{"OpenRedirectTask4#doubleDecode"}} --> P_REDIR
ORR{{"OpenRedirectRealRedirect#real"}} --> P_REDIR
PWL{{"ResetLinkAssignment#login"}} --> P_PWR["org.owasp.webgoat.lessons.passwordreset -- ACCOUNT-RECOVERY"]:::med
PWC{{"ResetLinkAssignment#changePassword"}} --> P_PWR
PWQ{{"SecurityQuestionAssignment#completed"}} --> P_PWR
PWSML{{"SimpleMailAssignment#login"}} --> P_PWR
PWSEND{{"ResetLinkAssignmentForgotPassword#sendPasswordResetLink"}} --> LIB_REST
PU_UP{{"ProfileUpload#uploadFileHandler"}} --> LIB_FILE
PU_GET{{"ProfileUpload#getProfilePicture"}} --> LIB_FILE
PUF_UP{{"ProfileUploadFix#uploadFileHandler"}} --> LIB_FILE
PUR_GET{{"ProfileUploadRetrieval#getProfilePicture"}} --> LIB_FILE
PZ_UP{{"ProfileZipSlip#uploadFileHandler"}} --> LIB_ZIP(("java.util.zip")):::high
PZ_GET{{"ProfileZipSlip#getProfilePicture"}} --> LIB_FILE
SECPW{{"SecurePasswordsAssignment#completed"}} --> LIB_ZXCVBN(("Zxcvbn"))
ACT_E{{"ActuatorExposureTask#actuatorEnv"}} --> P_SECMC["org.owasp.webgoat.lessons.securitymisconfiguration -- CONFIG-EXPOSURE"]:::med
ACT_K{{"ActuatorExposureTask#submitApiKey"}} --> P_SECMC
SPOOF{{"SpoofCookieAssignment#login"}} --> P_SPOOF["org.owasp.webgoat.lessons.spoofcookie -- SESSION -> CRYPTO"]:::med
SQL2{{"SqlInjectionLesson2#completed"}} --> LIB_JDBC
SQL5{{"SqlInjectionLesson5#completed"}} --> LIB_JDBC
SQL8{{"SqlInjectionLesson8#completed"}} --> LIB_JDBC
SQL10{{"SqlInjectionLesson10#completed"}} --> LIB_JDBC
SQL13{{"SqlInjectionLesson13#completed"}} --> LIB_JDBC
SQLC{{"SqlInjectionChallenge#registerNewUser"}} --> LIB_JDBC
SQLCL{{"SqlInjectionChallengeLogin#login"}} --> LIB_JDBC
SQL6A{{"SqlInjectionLesson6a#completed"}} --> LIB_JDBC
SQLIV{{"SqlOnlyInputValidation#attack"}} --> LIB_JDBC
SQL10B{{"SqlInjectionLesson10b#completed"}} --> LIB_JAVAC(("javax.tools.JavaCompiler")):::high
VULN{{"VulnerableComponentsLesson#completed"}} --> LIB_XSTREAM(("XStream")):::high
XSS5{{"CrossSiteScriptingLesson5a#completed"}} --> P_XSS["org.owasp.webgoat.lessons.xss -- XSS-SINK"]
XSS_ST{{"StoredXssComments#retrieveComments"}} --> P_XSS
XSS3{{"CrossSiteScriptingLesson3#completed"}} --> LIB_JSOUP(("Jsoup"))
XXE_S{{"SimpleXXE#createNewComment"}} --> LIB_XML(("javax.xml (StAX/XXE)")):::high
XXE_B{{"BlindSendFileAssignment#addComment"}} --> LIB_XML
XXE_C{{"ContentTypeAssignment#createNewUser"}} --> LIB_XML
XXE_R{{"CommentsEndpoint#retrieveComments"}} --> P_XXE["org.owasp.webgoat.lessons.xxe -- XML-PARSE"]:::high
SSRF2{{"SSRFTask2#completed"}} --> LIB_NET(("java.net.URL")):::high
classDef high fill:#fdd,stroke:#c00,color:#900
classDef med fill:#ffe9c7,stroke:#e08e00
Tip
The skill /codebase-hotspotsv2 (ref) is an evolution of the V1 that spawns dedicated agents per vulnerability class to enforce complete, class-specific detection rules that a general model tends to miss or apply inconsistently. It must be privileged over the V1.
π€ In this case, the context is that I received a codebase and I want to use claude code to give point to code that does risky processing from a security perspective (called hotspot).
π¦ User prompt is stored, as a claude code skill, into the file in the folder .claude/skills/codebase-hotspotsv2/ (ref).
π€ Use it via this instruction inside a claude code session: /codebase-hotspotsv2 [RELATIVE_PATH_TO_CODEBASE].
π€ In this case, I scanned the codebase with SemGrep to identify issues not linked to an entry point, like for example, a deprecated algorithm used but not called from an entry point.
π¦ User prompt is stored, as a claude code skill, into the file in the folder .claude/skills/codebase-semgrep-findings-review/ (ref).
π€ Use it via this instruction inside a claude code session: /codebase-semgrep-findings-review [PATH_TO_SEMGREP_REPORT] [RELATIVE_PATH_TO_CODEBASE] [MINIMUM_CONFIDENCE_LEVEL].
π‘ [MINIMUM_CONFIDENCE_LEVEL]: Minimum confidence threshold for inclusion in output, accepted values are:
CONFIRMED: Only confirmed findings.PARTIAL: Confirmed + needs-human-review findings.- Default:
PARTIAL-FALSE_POSITIVEverdicts are always excluded from the findings list but are recorded in the summary table.
Note
The skill has instruction to try to prevent the model of the coding assistant to have a confirmation bias problem. The instruction allows the coding assistant to search for grounded proof from the codebase and the documentation to truly reply to the hypothesis or question asked. It helps me to use the current coding assistant session to deep dive about my hypothesis.
π€ In this case, I have one of the following elements. I want to analyze the behavior of the feature to validate or invalidate the presence of an issue/risk from a security perspective:
- The filtered findings from SemGrep.
- The findings from the
codebase-hotspotsv2skills. - A doubt about a code snippet that seems dangerous or strange for me.
π¦ User prompt is stored, as a claude code skill, into the file in the folder .claude/skills/codebase-review-buddy/ (ref).
π€ Use it via this instruction inside a claude code session: /codebase-review-buddy [FILE_PATH] [LINE_NUMBER] "[HYPOTHESIS_OR_QUESTION]".
SKILL.md files use the Claude Code skill format (Anthropic proprietary) and cannot be validated with skills-ref (pip install skills-ref), which enforces the agentskills.io open specification. Claude Code-specific frontmatter fields (argument-hint, disable-model-invocation, etc.) are not allowed by that specification.
π§βπ» Copy the folder .claude/skills into the folder .claude of the project to review and use skills from a claude code session.
π‘ The script install.ps1 can be used too:
irm https://raw.githubusercontent.com/righettod/toolbox-ai-assisted-secure-code-review/main/install.ps1 | iexImportant
Apply the steps indicated after having installed the Claude code skills inside the current folder.
π€ Use the user prompt Please execute the conversion of Claude skills to "[CODING_ASSISTANT_NAME]" format as described in the "[INSTRUCTION_MD_FILE_NAME]" file. to initiate the conversion.
- Download the file CONVERT_CLAUDE_SKILLS_TO_VIBE.md inside the current folder.
- Start a
vibesession from the current folder. - Use the user prompt above with the following variable:
[CODING_ASSISTANT_NAME]toVibe CLI.[INSTRUCTION_MD_FILE_NAME]toCONVERT_CLAUDE_SKILLS_TO_VIBE.md.
Finally, exit and restart a new session from the current folder.
π Error: provider_message: Tool call id was ee61e87d-25d8-4bad-8e47-02939eb234c5 but must be a-z, A-Z, 0-9, with a length of 9.
βΉοΈ Issue 1075 open.
π‘ Solution: Patch the file vibe/core/llm/backend/mistral.py using the instruction below.
- Use this command to find the location of the file
python -c "import vibe.core.llm.backend.mistral as m; print(m.__file__)". - Add this function at the top of the class
MistralMapperand add theimport re:
@staticmethod
def _normalize_tool_call_id(tool_id: str | None) -> str:
"""Mistral requires tool call IDs to be exactly 9 alphanumeric characters."""
if not tool_id:
return "000000000"
clean = re.sub(r"[^a-zA-Z0-9]", "", tool_id)
# Pad if somehow shorter than 9 after stripping
clean = (clean + "000000000")[:9]
return clean- In the function
parse_tool_callsreplaceid=tool_call.id,byid=self._normalize_tool_call_id(tool_call.id),. - In the function
prepare_messageforRole.assistantreplaceid=tc.id,byid=self._normalize_tool_call_id(tc.id),. - In the function
prepare_messageforRole.toolreplacetool_call_id=msg.tool_call_id,bytool_call_id=self._normalize_tool_call_id(msg.tool_call_id),.