diff --git a/lib/modules/manager/bazel/extract.spec.ts b/lib/modules/manager/bazel/extract.spec.ts
index 79a34a789d1cb1e4cfb8ab6e547627428fc9ac7f..85e8d2d50ac08dce5c3c3ea7ee107bb34b099880 100644
--- a/lib/modules/manager/bazel/extract.spec.ts
+++ b/lib/modules/manager/bazel/extract.spec.ts
@@ -1,7 +1,8 @@
 import { Fixtures } from '../../../../test/fixtures';
-import { extractPackageFile as extract } from '.';
+import { extractPackageFile as _extractPackageFile } from '.';
 
-const extractPackageFile = (content: string) => extract(content, 'WORKSPACE');
+const extractPackageFile = (content: string) =>
+  _extractPackageFile(content, 'WORKSPACE');
 
 describe('modules/manager/bazel/extract', () => {
   describe('extractPackageFile()', () => {
diff --git a/lib/modules/manager/bazel/parser.spec.ts b/lib/modules/manager/bazel/parser.spec.ts
index 4bbf8bd6dddd085c0dd8754c8cfdb38e523aa564..79d12c72126be0ebee77b79bb43be92f9b97f235 100644
--- a/lib/modules/manager/bazel/parser.spec.ts
+++ b/lib/modules/manager/bazel/parser.spec.ts
@@ -1,4 +1,4 @@
-import { parse } from './parser';
+import { extract, parse } from './parser';
 
 describe('modules/manager/bazel/parser', () => {
   it('parses rules input', () => {
@@ -44,6 +44,10 @@ describe('modules/manager/bazel/parser', () => {
         },
       ],
     });
+    expect(extract(res!)).toMatchObject([
+      { rule: 'go_repository', name: 'foo' },
+      { rule: 'go_repository', name: 'bar', deps: ['baz', 'qux'] },
+    ]);
   });
 
   it('parses multiple archives', () => {
@@ -100,6 +104,25 @@ describe('modules/manager/bazel/parser', () => {
         },
       ],
     });
+    expect(extract(res!)).toMatchObject([
+      {
+        rule: 'http_archive',
+        name: 'aspect_rules_js',
+        sha256:
+          'db9f446752fe4100320cf8487e8fd476b9af0adf6b99b601bcfd70b289bb0598',
+        strip_prefix: 'rules_js-1.1.2',
+        url: 'https://github.com/aspect-build/rules_js/archive/refs/tags/v1.1.2.tar.gz',
+      },
+      {
+        rule: 'http_archive',
+        name: 'rules_nodejs',
+        sha256:
+          '5aef09ed3279aa01d5c928e3beb248f9ad32dde6aafe6373a8c994c3ce643064',
+        urls: [
+          'https://github.com/bazelbuild/rules_nodejs/releases/download/5.5.3/rules_nodejs-core-5.5.3.tar.gz',
+        ],
+      },
+    ]);
   });
 
   it('parses http_archive', () => {
@@ -129,6 +152,15 @@ describe('modules/manager/bazel/parser', () => {
         },
       ],
     });
+    expect(extract(res!)).toMatchObject([
+      {
+        rule: 'http_archive',
+        name: 'rules_nodejs',
+        sha256:
+          '5aef09ed3279aa01d5c928e3beb248f9ad32dde6aafe6373a8c994c3ce643064',
+        url: 'https://github.com/bazelbuild/rules_nodejs/releases/download/5.5.3/rules_nodejs-core-5.5.3.tar.gz',
+      },
+    ]);
   });
 
   it('parses http_archive with prefixes and multiple urls', () => {
@@ -174,5 +206,19 @@ describe('modules/manager/bazel/parser', () => {
         },
       ],
     });
+    expect(extract(res!)).toMatchObject([
+      {
+        name: 'bazel_toolchains',
+        rule: 'http_archive',
+        sha256:
+          '4b1468b254a572dbe134cc1fd7c6eab1618a72acd339749ea343bd8f55c3b7eb',
+        strip_prefix:
+          'bazel-toolchains-d665ccfa3e9c90fa789671bf4ef5f7c19c5715c4',
+        urls: [
+          'https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/d665ccfa3e9c90fa789671bf4ef5f7c19c5715c4.tar.gz',
+          'https://github.com/bazelbuild/bazel-toolchains/archive/d665ccfa3e9c90fa789671bf4ef5f7c19c5715c4.tar.gz',
+        ],
+      },
+    ]);
   });
 });
diff --git a/lib/modules/manager/bazel/parser.ts b/lib/modules/manager/bazel/parser.ts
index 70f5090145b364be9e3fb47862c079e5e5a04151..75718503e4f7156f4ef714bfba6bd1b04ff68fa1 100644
--- a/lib/modules/manager/bazel/parser.ts
+++ b/lib/modules/manager/bazel/parser.ts
@@ -2,7 +2,13 @@ import { lang, lexer, parser, query as q } from 'good-enough-parser';
 import hasha from 'hasha';
 import * as memCache from '../../../util/cache/memory';
 import { supportedRulesRegex } from './rules/index';
-import type { ArrayFragment, NestedFragment, RecordFragment } from './types';
+import type {
+  ArrayFragment,
+  Fragment,
+  FragmentData,
+  NestedFragment,
+  RecordFragment,
+} from './types';
 
 interface Ctx {
   readonly source: string;
@@ -232,3 +238,20 @@ export function parse(input: string): ArrayFragment | null {
   memCache.set(cacheKey, result);
   return result;
 }
+
+export function extract(fragment: Fragment): FragmentData {
+  if (fragment.type === 'string') {
+    return fragment.value;
+  }
+
+  if (fragment.type === 'record') {
+    const { children } = fragment;
+    const result: Record<string, FragmentData> = {};
+    for (const [key, value] of Object.entries(children)) {
+      result[key] = extract(value);
+    }
+    return result;
+  }
+
+  return fragment.children.map(extract);
+}
diff --git a/lib/modules/manager/bazel/rules/docker.ts b/lib/modules/manager/bazel/rules/docker.ts
index 797c8c013562f5fbbec5c96edf55e596bc2d3e93..233487c5e8091156f19a7ceedcb5fbee3ae7e229 100644
--- a/lib/modules/manager/bazel/rules/docker.ts
+++ b/lib/modules/manager/bazel/rules/docker.ts
@@ -1,38 +1,28 @@
-import is from '@sindresorhus/is';
+import { z } from 'zod';
 import { DockerDatasource } from '../../../datasource/docker';
 import { id as dockerVersioning } from '../../../versioning/docker';
 import type { PackageDependency } from '../../types';
-import type { Target } from '../types';
 
-export function dockerDependency({
-  rule: depType,
-  name: depName,
-  tag: currentValue,
-  digest: currentDigest,
-  repository: packageName,
-  registry,
-}: Target): PackageDependency | null {
-  let dep: PackageDependency | null = null;
+export const dockerRules = ['container_pull'] as const;
 
-  if (
-    depType === 'container_pull' &&
-    is.string(depName) &&
-    is.string(currentValue) &&
-    is.string(currentDigest) &&
-    is.string(packageName) &&
-    is.string(registry)
-  ) {
-    dep = {
+export const DockerTarget = z
+  .object({
+    rule: z.enum(dockerRules),
+    name: z.string(),
+    tag: z.string(),
+    digest: z.string(),
+    repository: z.string(),
+    registry: z.string(),
+  })
+  .transform(
+    ({ rule, name, repository, tag, digest, registry }): PackageDependency => ({
       datasource: DockerDatasource.id,
       versioning: dockerVersioning,
-      depType,
-      depName,
-      packageName,
-      currentValue,
-      currentDigest,
+      depType: rule,
+      depName: name,
+      packageName: repository,
+      currentValue: tag,
+      currentDigest: digest,
       registryUrls: [registry],
-    };
-  }
-
-  return dep;
-}
+    })
+  );
diff --git a/lib/modules/manager/bazel/rules/git.ts b/lib/modules/manager/bazel/rules/git.ts
index c5ee9affb5476dd1634351cdf5a65d68dd8cd477..bdf52e6f5b91be9f8ad835ec1e87d460a0d29620 100644
--- a/lib/modules/manager/bazel/rules/git.ts
+++ b/lib/modules/manager/bazel/rules/git.ts
@@ -1,50 +1,51 @@
-import is from '@sindresorhus/is';
 import parseGithubUrl from 'github-url-from-git';
+import { z } from 'zod';
+import { regEx } from '../../../../util/regex';
 import { GithubReleasesDatasource } from '../../../datasource/github-releases';
 import type { PackageDependency } from '../../types';
-import type { Target } from '../types';
-
-export function gitDependency({
-  rule: depType,
-  name: depName,
-  tag: currentValue,
-  commit: currentDigest,
-  remote,
-}: Target): PackageDependency | null {
-  let dep: PackageDependency | null = null;
-
-  if (
-    depType === 'git_repository' &&
-    is.string(depName) &&
-    (is.string(currentValue) || is.string(currentDigest)) &&
-    is.string(remote)
-  ) {
-    dep = {
-      datasource: GithubReleasesDatasource.id,
-      depType,
-      depName,
+
+const githubUrlRegex = regEx(
+  /^https:\/\/github\.com\/(?<packageName>[^/]+\/[^/]+)/
+);
+
+function githubPackageName(input: string): string | undefined {
+  return parseGithubUrl(input)?.match(githubUrlRegex)?.groups?.packageName;
+}
+
+export const gitRules = ['git_repository'] as const;
+
+export const GitTarget = z
+  .object({
+    rule: z.enum(gitRules),
+    name: z.string(),
+    tag: z.string().optional(),
+    commit: z.string().optional(),
+    remote: z.string(),
+  })
+  .refine(({ tag, commit }) => !!tag || !!commit)
+  .transform(({ rule, name, tag, commit, remote }): PackageDependency => {
+    const dep: PackageDependency = {
+      depType: rule,
+      depName: name,
     };
 
-    if (is.string(currentValue)) {
-      dep.currentValue = currentValue;
+    if (tag) {
+      dep.currentValue = tag;
     }
 
-    if (is.string(currentDigest)) {
-      dep.currentDigest = currentDigest;
+    if (commit) {
+      dep.currentDigest = commit;
     }
 
-    // TODO: Check if we really need to use parse here or if it should always be a plain https url (#9605)
-    const packageName = parseGithubUrl(remote)?.substring(
-      'https://github.com/'.length
-    );
+    const githubPackage = githubPackageName(remote);
+    if (githubPackage) {
+      dep.datasource = GithubReleasesDatasource.id;
+      dep.packageName = githubPackage;
+    }
 
-    // istanbul ignore else
-    if (packageName) {
-      dep.packageName = packageName;
-    } else {
-      dep.skipReason = 'unsupported-remote';
+    if (!dep.datasource) {
+      dep.skipReason = 'unsupported-datasource';
     }
-  }
 
-  return dep;
-}
+    return dep;
+  });
diff --git a/lib/modules/manager/bazel/rules/go.ts b/lib/modules/manager/bazel/rules/go.ts
index d82036fe04d4119f1fe42d276687bc724dcde41a..ff6f4398003b7a14a50cf4468fae91a86c6f255a 100644
--- a/lib/modules/manager/bazel/rules/go.ts
+++ b/lib/modules/manager/bazel/rules/go.ts
@@ -1,54 +1,51 @@
-import is from '@sindresorhus/is';
+import { z } from 'zod';
 import { regEx } from '../../../../util/regex';
 import { GoDatasource } from '../../../datasource/go';
 import type { PackageDependency } from '../../types';
-import type { Target } from '../types';
 
-export function goDependency({
-  rule: depType,
-  name: depName,
-  tag: currentValue,
-  commit: currentDigest,
-  importpath: packageName,
-  remote,
-}: Target): PackageDependency | null {
-  let dep: PackageDependency | null = null;
+export const goRules = ['go_repository'] as const;
 
-  if (
-    depType === 'go_repository' &&
-    is.string(depName) &&
-    (is.string(currentValue) || is.string(currentDigest)) &&
-    is.string(packageName)
-  ) {
-    dep = {
-      datasource: GoDatasource.id,
-      depType,
-      depName,
-      packageName,
-    };
+export const GoTarget = z
+  .object({
+    rule: z.enum(goRules),
+    name: z.string(),
+    tag: z.string().optional(),
+    commit: z.string().optional(),
+    importpath: z.string(),
+    remote: z.string().optional(),
+  })
+  .refine(({ tag, commit }) => !!tag || !!commit)
+  .transform(
+    ({ rule, name, tag, commit, importpath, remote }): PackageDependency => {
+      const dep: PackageDependency = {
+        datasource: GoDatasource.id,
+        depType: rule,
+        depName: name,
+        packageName: importpath,
+      };
 
-    if (is.string(currentValue)) {
-      dep.currentValue = currentValue;
-    }
+      if (tag) {
+        dep.currentValue = tag;
+      }
 
-    if (is.string(currentDigest)) {
-      dep.currentValue = 'v0.0.0';
-      dep.currentDigest = currentDigest;
-      dep.currentDigestShort = currentDigest.substring(0, 7);
-      dep.digestOneAndOnly = true;
-    }
+      if (commit) {
+        dep.currentValue = 'v0.0.0';
+        dep.currentDigest = commit;
+        dep.currentDigestShort = commit.substring(0, 7);
+        dep.digestOneAndOnly = true;
+      }
 
-    if (is.string(remote)) {
-      const remoteMatch = regEx(
-        /https:\/\/github\.com(?:.*\/)(([a-zA-Z]+)([-])?([a-zA-Z]+))/
-      ).exec(remote);
-      if (remoteMatch && remoteMatch[0].length === remote.length) {
-        dep.packageName = remote.replace('https://', '');
-      } else {
-        dep.skipReason = 'unsupported-remote';
+      if (remote) {
+        const remoteMatch = regEx(
+          /https:\/\/github\.com(?:.*\/)(([a-zA-Z]+)([-])?([a-zA-Z]+))/
+        ).exec(remote);
+        if (remoteMatch && remoteMatch[0].length === remote.length) {
+          dep.packageName = remote.replace('https://', '');
+        } else {
+          dep.skipReason = 'unsupported-remote';
+        }
       }
-    }
-  }
 
-  return dep;
-}
+      return dep;
+    }
+  );
diff --git a/lib/modules/manager/bazel/rules/http.ts b/lib/modules/manager/bazel/rules/http.ts
index 10dbcfbe03aa9f11d65aecc622c645257dc79293..4fc3e4bc9b14d61e4e3376b8e9d3620b5218edff 100644
--- a/lib/modules/manager/bazel/rules/http.ts
+++ b/lib/modules/manager/bazel/rules/http.ts
@@ -1,10 +1,11 @@
 import is from '@sindresorhus/is';
+import { z } from 'zod';
 import { regEx } from '../../../../util/regex';
 import { parseUrl } from '../../../../util/url';
 import { GithubReleasesDatasource } from '../../../datasource/github-releases';
 import { GithubTagsDatasource } from '../../../datasource/github-tags';
 import type { PackageDependency } from '../../types';
-import type { Target, UrlParsedResult } from '../types';
+import type { UrlParsedResult } from '../types';
 
 export function parseArchiveUrl(
   urlString: string | undefined | null
@@ -50,47 +51,35 @@ export function parseArchiveUrl(
   return null;
 }
 
-export function httpDependency({
-  rule: depType,
-  name: depName,
-  url,
-  urls,
-  sha256,
-}: Target): PackageDependency | null {
-  let dep: PackageDependency | null = null;
+export const httpRules = ['http_archive', 'http_file'] as const;
 
-  if (
-    (depType === 'http_archive' || depType === 'http_file') &&
-    is.string(depName) &&
-    is.string(sha256)
-  ) {
-    let parsedUrl: UrlParsedResult | null = null;
-    if (is.string(url)) {
-      parsedUrl = parseArchiveUrl(url);
-    } else if (is.array(urls, is.string)) {
-      for (const u of urls) {
-        parsedUrl = parseArchiveUrl(u);
-        if (parsedUrl) {
-          break;
-        }
-      }
+export const HttpTarget = z
+  .object({
+    rule: z.enum(httpRules),
+    name: z.string(),
+    url: z.string().optional(),
+    urls: z.array(z.string()).optional(),
+    sha256: z.string(),
+  })
+  .refine(({ url, urls }) => !!url || !!urls)
+  .transform(({ rule, name, url, urls = [] }): PackageDependency | null => {
+    const parsedUrl = [url, ...urls].map(parseArchiveUrl).find(is.truthy);
+    if (!parsedUrl) {
+      return null;
     }
 
-    if (parsedUrl) {
-      dep = {
-        datasource: parsedUrl.datasource,
-        depType,
-        depName,
-        packageName: parsedUrl.repo,
-      };
+    const dep: PackageDependency = {
+      datasource: parsedUrl.datasource,
+      depType: rule,
+      depName: name,
+      packageName: parsedUrl.repo,
+    };
 
-      if (regEx(/^[a-f0-9]{40}$/i).test(parsedUrl.currentValue)) {
-        dep.currentDigest = parsedUrl.currentValue;
-      } else {
-        dep.currentValue = parsedUrl.currentValue;
-      }
+    if (regEx(/^[a-f0-9]{40}$/i).test(parsedUrl.currentValue)) {
+      dep.currentDigest = parsedUrl.currentValue;
+    } else {
+      dep.currentValue = parsedUrl.currentValue;
     }
-  }
 
-  return dep;
-}
+    return dep;
+  });
diff --git a/lib/modules/manager/bazel/rules/index.spec.ts b/lib/modules/manager/bazel/rules/index.spec.ts
index f0b268641db0831d9687b02bafe3f043135b6169..4a01c579767cc36a61aef28bec0233a3f3e9ae43 100644
--- a/lib/modules/manager/bazel/rules/index.spec.ts
+++ b/lib/modules/manager/bazel/rules/index.spec.ts
@@ -1,11 +1,8 @@
-import { dockerDependency } from './docker';
-import { gitDependency } from './git';
-import { goDependency } from './go';
-import { httpDependency, parseArchiveUrl } from './http';
-import { extractDepFromFragment } from '.';
+import { parseArchiveUrl } from './http';
+import { extractDepFromFragmentData } from '.';
 
 describe('modules/manager/bazel/rules/index', () => {
-  test('parseUrl', () => {
+  it('parses archiveUrl', () => {
     expect(parseArchiveUrl('')).toBeNull();
     expect(parseArchiveUrl(null)).toBeNull();
     expect(parseArchiveUrl(null)).toBeNull();
@@ -46,325 +43,284 @@ describe('modules/manager/bazel/rules/index', () => {
     });
   });
 
-  test('gitDependency', () => {
-    expect(gitDependency({ rule: 'foo_bar', name: 'foo_bar' })).toBeNull();
-
-    expect(
-      gitDependency({ rule: 'git_repository', name: 'foo_bar' })
-    ).toBeNull();
-
-    expect(
-      gitDependency({ rule: 'git_repository', name: 'foo_bar', tag: '1.2.3' })
-    ).toBeNull();
-
-    expect(
-      gitDependency({
-        rule: 'git_repository',
-        name: 'foo_bar',
-        tag: '1.2.3',
-        remote: 'https://github.com/foo/bar',
-      })
-    ).toEqual({
-      datasource: 'github-releases',
-      depType: 'git_repository',
-      depName: 'foo_bar',
-      packageName: 'foo/bar',
-      currentValue: '1.2.3',
-    });
-
-    expect(
-      gitDependency({
-        rule: 'git_repository',
-        name: 'foo_bar',
-        commit: 'abcdef0123abcdef0123abcdef0123abcdef0123',
-        remote: 'https://github.com/foo/bar',
-      })
-    ).toEqual({
-      datasource: 'github-releases',
-      depType: 'git_repository',
-      depName: 'foo_bar',
-      packageName: 'foo/bar',
-      currentDigest: 'abcdef0123abcdef0123abcdef0123abcdef0123',
-    });
-  });
-
-  test('goDependency', () => {
-    expect(goDependency({ rule: 'foo_bar', name: 'foo_bar' })).toBeNull();
-
-    expect(goDependency({ rule: 'go_repository', name: 'foo_bar' })).toBeNull();
+  describe('git', () => {
+    it('extracts git dependencies', () => {
+      expect(
+        extractDepFromFragmentData({ rule: 'foo_bar', name: 'foo_bar' })
+      ).toBeNull();
 
-    expect(
-      goDependency({ rule: 'go_repository', name: 'foo_bar', tag: '1.2.3' })
-    ).toBeNull();
+      expect(
+        extractDepFromFragmentData({ rule: 'git_repository', name: 'foo_bar' })
+      ).toBeNull();
 
-    expect(
-      goDependency({
-        rule: 'go_repository',
-        name: 'foo_bar',
-        tag: '1.2.3',
-        importpath: 'foo/bar/baz',
-      })
-    ).toEqual({
-      datasource: 'go',
-      depType: 'go_repository',
-      depName: 'foo_bar',
-      packageName: 'foo/bar/baz',
-      currentValue: '1.2.3',
-    });
+      expect(
+        extractDepFromFragmentData({
+          rule: 'git_repository',
+          name: 'foo_bar',
+          tag: '1.2.3',
+        })
+      ).toBeNull();
 
-    expect(
-      goDependency({
-        rule: 'go_repository',
-        name: 'foo_bar',
-        commit: 'abcdef0123abcdef0123abcdef0123abcdef0123',
-        importpath: 'foo/bar/baz',
-      })
-    ).toEqual({
-      datasource: 'go',
-      depType: 'go_repository',
-      depName: 'foo_bar',
-      packageName: 'foo/bar/baz',
-      currentValue: 'v0.0.0',
-      currentDigest: 'abcdef0123abcdef0123abcdef0123abcdef0123',
-      currentDigestShort: 'abcdef0',
-      digestOneAndOnly: true,
-    });
+      expect(
+        extractDepFromFragmentData({
+          rule: 'git_repository',
+          name: 'foo_bar',
+          tag: '1.2.3',
+          remote: 'https://github.com/foo/bar',
+        })
+      ).toEqual({
+        datasource: 'github-releases',
+        depType: 'git_repository',
+        depName: 'foo_bar',
+        packageName: 'foo/bar',
+        currentValue: '1.2.3',
+      });
 
-    expect(
-      goDependency({
-        rule: 'go_repository',
-        name: 'foo_bar',
-        tag: '1.2.3',
-        importpath: 'foo/bar/baz',
-        remote: 'https://github.com/foo/bar',
-      })
-    ).toEqual({
-      datasource: 'go',
-      depType: 'go_repository',
-      depName: 'foo_bar',
-      packageName: 'github.com/foo/bar',
-      currentValue: '1.2.3',
-    });
+      expect(
+        extractDepFromFragmentData({
+          rule: 'git_repository',
+          name: 'foo_bar',
+          commit: 'abcdef0123abcdef0123abcdef0123abcdef0123',
+          remote: 'https://github.com/foo/bar',
+        })
+      ).toEqual({
+        datasource: 'github-releases',
+        depType: 'git_repository',
+        depName: 'foo_bar',
+        packageName: 'foo/bar',
+        currentDigest: 'abcdef0123abcdef0123abcdef0123abcdef0123',
+      });
 
-    expect(
-      goDependency({
-        rule: 'go_repository',
-        name: 'foo_bar',
-        tag: '1.2.3',
-        importpath: 'foo/bar/baz',
-        remote: 'https://example.com/foo/bar',
-      })
-    ).toEqual({
-      datasource: 'go',
-      depType: 'go_repository',
-      depName: 'foo_bar',
-      packageName: 'foo/bar/baz',
-      currentValue: '1.2.3',
-      skipReason: 'unsupported-remote',
+      expect(
+        extractDepFromFragmentData({
+          rule: 'git_repository',
+          name: 'foo_bar',
+          tag: '1.2.3',
+          remote: 'https://gitlab.com/foo/bar',
+        })
+      ).toMatchObject({
+        currentValue: '1.2.3',
+        depName: 'foo_bar',
+        depType: 'git_repository',
+        skipReason: 'unsupported-datasource',
+      });
     });
   });
 
-  test('httpDependency', () => {
-    expect(httpDependency({ rule: 'foo_bar', name: 'foo_bar' })).toBeNull();
-
-    expect(
-      httpDependency({ rule: 'http_archive', name: 'foo_bar' })
-    ).toBeNull();
+  describe('go', () => {
+    it('extracts go dependencies', () => {
+      expect(
+        extractDepFromFragmentData({ rule: 'foo_bar', name: 'foo_bar' })
+      ).toBeNull();
 
-    expect(
-      httpDependency({
-        rule: 'http_archive',
-        name: 'foo_bar',
-        sha256: 'abcdef0123abcdef0123abcdef0123abcdef0123',
-      })
-    ).toBeNull();
+      expect(
+        extractDepFromFragmentData({ rule: 'go_repository', name: 'foo_bar' })
+      ).toBeNull();
 
-    expect(
-      httpDependency({
-        rule: 'http_archive',
-        name: 'foo_bar',
-        sha256: 'abcdef0123abcdef0123abcdef0123abcdef0123',
-        url: 'https://github.com/foo/bar/archive/abcdef0123abcdef0123abcdef0123abcdef0123.tar.gz',
-      })
-    ).toEqual({
-      currentDigest: 'abcdef0123abcdef0123abcdef0123abcdef0123',
-      datasource: 'github-tags',
-      depName: 'foo_bar',
-      depType: 'http_archive',
-      packageName: 'foo/bar',
-    });
+      expect(
+        extractDepFromFragmentData({
+          rule: 'go_repository',
+          name: 'foo_bar',
+          tag: '1.2.3',
+        })
+      ).toBeNull();
 
-    expect(
-      httpDependency({
-        rule: 'http_archive',
-        name: 'foo_bar',
-        sha256: 'abcdef0123abcdef0123abcdef0123abcdef0123',
-        urls: [
-          'https://example.com/foo/bar',
-          'https://github.com/foo/bar/archive/abcdef0123abcdef0123abcdef0123abcdef0123.tar.gz',
-        ],
-      })
-    ).toEqual({
-      currentDigest: 'abcdef0123abcdef0123abcdef0123abcdef0123',
-      datasource: 'github-tags',
-      depName: 'foo_bar',
-      depType: 'http_archive',
-      packageName: 'foo/bar',
-    });
+      expect(
+        extractDepFromFragmentData({
+          rule: 'go_repository',
+          name: 'foo_bar',
+          tag: '1.2.3',
+          importpath: 'foo/bar/baz',
+        })
+      ).toEqual({
+        datasource: 'go',
+        depType: 'go_repository',
+        depName: 'foo_bar',
+        packageName: 'foo/bar/baz',
+        currentValue: '1.2.3',
+      });
 
-    expect(
-      httpDependency({
-        rule: 'http_archive',
-        name: 'foo_bar',
-        sha256: 'abcdef0123abcdef0123abcdef0123abcdef0123',
-        url: 'https://github.com/foo/bar/releases/download/1.2.3/foobar-1.2.3.tar.gz',
-      })
-    ).toEqual({
-      currentValue: '1.2.3',
-      datasource: 'github-releases',
-      depName: 'foo_bar',
-      depType: 'http_archive',
-      packageName: 'foo/bar',
-    });
+      expect(
+        extractDepFromFragmentData({
+          rule: 'go_repository',
+          name: 'foo_bar',
+          commit: 'abcdef0123abcdef0123abcdef0123abcdef0123',
+          importpath: 'foo/bar/baz',
+        })
+      ).toEqual({
+        datasource: 'go',
+        depType: 'go_repository',
+        depName: 'foo_bar',
+        packageName: 'foo/bar/baz',
+        currentValue: 'v0.0.0',
+        currentDigest: 'abcdef0123abcdef0123abcdef0123abcdef0123',
+        currentDigestShort: 'abcdef0',
+        digestOneAndOnly: true,
+      });
 
-    expect(
-      httpDependency({
-        rule: 'http_archive',
-        name: 'foo_bar',
-        sha256: 'abcdef0123abcdef0123abcdef0123abcdef0123',
-        urls: [
-          'https://example.com/foo/bar',
-          'https://github.com/foo/bar/releases/download/1.2.3/foobar-1.2.3.tar.gz',
-        ],
-      })
-    ).toEqual({
-      currentValue: '1.2.3',
-      datasource: 'github-releases',
-      depName: 'foo_bar',
-      depType: 'http_archive',
-      packageName: 'foo/bar',
-    });
+      expect(
+        extractDepFromFragmentData({
+          rule: 'go_repository',
+          name: 'foo_bar',
+          tag: '1.2.3',
+          importpath: 'foo/bar/baz',
+          remote: 'https://github.com/foo/bar',
+        })
+      ).toEqual({
+        datasource: 'go',
+        depType: 'go_repository',
+        depName: 'foo_bar',
+        packageName: 'github.com/foo/bar',
+        currentValue: '1.2.3',
+      });
 
-    expect(
-      httpDependency({
-        rule: 'http_archive',
-        name: 'aspect_rules_js',
-        sha256:
-          'db9f446752fe4100320cf8487e8fd476b9af0adf6b99b601bcfd70b289bb0598',
-        urls: [
-          'https://github.com/aspect-build/rules_js/archive/refs/tags/v1.1.2.tar.gz',
-        ],
-      })
-    ).toEqual({
-      currentValue: 'v1.1.2',
-      datasource: 'github-tags',
-      depName: 'aspect_rules_js',
-      depType: 'http_archive',
-      packageName: 'aspect-build/rules_js',
+      expect(
+        extractDepFromFragmentData({
+          rule: 'go_repository',
+          name: 'foo_bar',
+          tag: '1.2.3',
+          importpath: 'foo/bar/baz',
+          remote: 'https://example.com/foo/bar',
+        })
+      ).toEqual({
+        datasource: 'go',
+        depType: 'go_repository',
+        depName: 'foo_bar',
+        packageName: 'foo/bar/baz',
+        currentValue: '1.2.3',
+        skipReason: 'unsupported-remote',
+      });
     });
   });
 
-  test('dockerDependency', () => {
-    expect(dockerDependency({ rule: 'foo_bar', name: 'foo_bar' })).toBeNull();
+  describe('http', () => {
+    it('extracts http dependencies', () => {
+      expect(
+        extractDepFromFragmentData({ rule: 'foo_bar', name: 'foo_bar' })
+      ).toBeNull();
 
-    expect(
-      dockerDependency({
-        rule: 'container_pull',
-        name: 'foo_bar',
-        tag: '1.2.3',
-        digest: 'abcdef0123abcdef0123abcdef0123abcdef0123',
-        repository: 'example.com/foo/bar',
-        registry: 'https://example.com',
-      })
-    ).toEqual({
-      currentDigest: 'abcdef0123abcdef0123abcdef0123abcdef0123',
-      currentValue: '1.2.3',
-      datasource: 'docker',
-      depName: 'foo_bar',
-      depType: 'container_pull',
-      packageName: 'example.com/foo/bar',
-      registryUrls: ['https://example.com'],
-      versioning: 'docker',
-    });
-  });
+      expect(
+        extractDepFromFragmentData({ rule: 'http_archive', name: 'foo_bar' })
+      ).toBeNull();
 
-  describe('extractDepFromFragment', () => {
-    it('returns null for unknown rule type', () => {
       expect(
-        extractDepFromFragment({
-          type: 'record',
-          value: '',
-          offset: 0,
-          children: {
-            rule: { type: 'string', value: 'foo', offset: 0 },
-            name: { type: 'string', value: 'bar', offset: 0 },
-          },
+        extractDepFromFragmentData({
+          rule: 'http_archive',
+          name: 'foo_bar',
+          sha256: 'abcdef0123abcdef0123abcdef0123abcdef0123',
         })
       ).toBeNull();
-    });
 
-    it('extracts from git_repository', () => {
       expect(
-        extractDepFromFragment({
-          type: 'record',
-          value: '',
-          offset: 0,
-          children: {
-            rule: { type: 'string', value: 'git_repository', offset: 0 },
-            name: { type: 'string', value: 'foo_bar', offset: 0 },
-            tag: { type: 'string', value: '1.2.3', offset: 0 },
-            remote: {
-              type: 'string',
-              value: 'https://github.com/foo/bar',
-              offset: 0,
-            },
-          },
+        extractDepFromFragmentData({
+          rule: 'http_archive',
+          name: 'foo_bar',
+          sha256: 'abcdef0123abcdef0123abcdef0123abcdef0123',
+          url: 'https://github.com/foo/bar/archive/abcdef0123abcdef0123abcdef0123abcdef0123.tar.gz',
         })
       ).toEqual({
-        datasource: 'github-releases',
-        depType: 'git_repository',
+        currentDigest: 'abcdef0123abcdef0123abcdef0123abcdef0123',
+        datasource: 'github-tags',
         depName: 'foo_bar',
+        depType: 'http_archive',
+        packageName: 'foo/bar',
+      });
+
+      expect(
+        extractDepFromFragmentData({
+          rule: 'http_archive',
+          name: 'foo_bar',
+          sha256: 'abcdef0123abcdef0123abcdef0123abcdef0123',
+          urls: [
+            'https://example.com/foo/bar',
+            'https://github.com/foo/bar/archive/abcdef0123abcdef0123abcdef0123abcdef0123.tar.gz',
+          ],
+        })
+      ).toEqual({
+        currentDigest: 'abcdef0123abcdef0123abcdef0123abcdef0123',
+        datasource: 'github-tags',
+        depName: 'foo_bar',
+        depType: 'http_archive',
         packageName: 'foo/bar',
+      });
+
+      expect(
+        extractDepFromFragmentData({
+          rule: 'http_archive',
+          name: 'foo_bar',
+          sha256: 'abcdef0123abcdef0123abcdef0123abcdef0123',
+          url: 'https://github.com/foo/bar/releases/download/1.2.3/foobar-1.2.3.tar.gz',
+        })
+      ).toEqual({
         currentValue: '1.2.3',
+        datasource: 'github-releases',
+        depName: 'foo_bar',
+        depType: 'http_archive',
+        packageName: 'foo/bar',
       });
-    });
 
-    it('extracts from http_archive', () => {
       expect(
-        extractDepFromFragment({
-          type: 'record',
-          value: '',
-          offset: 0,
-          children: {
-            rule: { type: 'string', value: 'http_archive', offset: 0 },
-            name: { type: 'string', value: 'rules_nodejs', offset: 0 },
-            sha256: {
-              type: 'string',
-              value:
-                '5aef09ed3279aa01d5c928e3beb248f9ad32dde6aafe6373a8c994c3ce643064',
-              offset: 0,
-            },
-            urls: {
-              type: 'array',
-              value: '',
-              offset: 0,
-              children: [
-                {
-                  type: 'string',
-                  offset: 0,
-                  value:
-                    'https://github.com/bazelbuild/rules_nodejs/releases/download/5.5.3/rules_nodejs-core-5.5.3.tar.gz',
-                },
-              ],
-            },
-          },
+        extractDepFromFragmentData({
+          rule: 'http_archive',
+          name: 'foo_bar',
+          sha256: 'abcdef0123abcdef0123abcdef0123abcdef0123',
+          urls: [
+            'https://example.com/foo/bar',
+            'https://github.com/foo/bar/releases/download/1.2.3/foobar-1.2.3.tar.gz',
+          ],
         })
       ).toEqual({
+        currentValue: '1.2.3',
         datasource: 'github-releases',
+        depName: 'foo_bar',
         depType: 'http_archive',
-        depName: 'rules_nodejs',
-        packageName: 'bazelbuild/rules_nodejs',
-        currentValue: '5.5.3',
+        packageName: 'foo/bar',
+      });
+
+      expect(
+        extractDepFromFragmentData({
+          rule: 'http_archive',
+          name: 'aspect_rules_js',
+          sha256:
+            'db9f446752fe4100320cf8487e8fd476b9af0adf6b99b601bcfd70b289bb0598',
+          urls: [
+            'https://github.com/aspect-build/rules_js/archive/refs/tags/v1.1.2.tar.gz',
+          ],
+        })
+      ).toEqual({
+        currentValue: 'v1.1.2',
+        datasource: 'github-tags',
+        depName: 'aspect_rules_js',
+        depType: 'http_archive',
+        packageName: 'aspect-build/rules_js',
+      });
+    });
+  });
+
+  describe('docker', () => {
+    it('extracts docker dependencies', () => {
+      expect(
+        extractDepFromFragmentData({ rule: 'foo_bar', name: 'foo_bar' })
+      ).toBeNull();
+
+      expect(
+        extractDepFromFragmentData({
+          rule: 'container_pull',
+          name: 'foo_bar',
+          tag: '1.2.3',
+          digest: 'abcdef0123abcdef0123abcdef0123abcdef0123',
+          repository: 'example.com/foo/bar',
+          registry: 'https://example.com',
+        })
+      ).toEqual({
+        currentDigest: 'abcdef0123abcdef0123abcdef0123abcdef0123',
+        currentValue: '1.2.3',
+        datasource: 'docker',
+        depName: 'foo_bar',
+        depType: 'container_pull',
+        packageName: 'example.com/foo/bar',
+        registryUrls: ['https://example.com'],
+        versioning: 'docker',
       });
     });
   });
diff --git a/lib/modules/manager/bazel/rules/index.ts b/lib/modules/manager/bazel/rules/index.ts
index ec9c7966541da2580e97b5fe06db3fd342371e3e..8296c4514d2fa673d22762ebe873b165ed2220e9 100644
--- a/lib/modules/manager/bazel/rules/index.ts
+++ b/lib/modules/manager/bazel/rules/index.ts
@@ -1,74 +1,35 @@
-import is from '@sindresorhus/is';
-import { logger } from '../../../../logger';
+import { z } from 'zod';
 import { regEx } from '../../../../util/regex';
 import type { PackageDependency } from '../../types';
-import type {
-  Fragment,
-  StringFragment,
-  Target,
-  TargetAttribute,
-} from '../types';
-import { dockerDependency } from './docker';
-import { gitDependency } from './git';
-import { goDependency } from './go';
-import { httpDependency } from './http';
-
-type DependencyExtractor = (_: Target) => PackageDependency | null;
-type DependencyExtractorRegistry = Record<string, DependencyExtractor>;
-
-const dependencyExtractorRegistry: DependencyExtractorRegistry = {
-  git_repository: gitDependency,
-  go_repository: goDependency,
-  http_archive: httpDependency,
-  http_file: httpDependency,
-  container_pull: dockerDependency,
-};
-
-const supportedRules = Object.keys(dependencyExtractorRegistry);
+import { extract } from '../parser';
+import type { Fragment, FragmentData, Target } from '../types';
+import { DockerTarget, dockerRules } from './docker';
+import { GitTarget, gitRules } from './git';
+import { GoTarget, goRules } from './go';
+import { HttpTarget, httpRules } from './http';
+
+const Target = z.union([DockerTarget, GitTarget, GoTarget, HttpTarget]);
+
+/**
+ * Gather all rule names supported by Renovate in order to speed up parsing
+ * by filtering out other syntactically correct rules we don't support yet.
+ */
+const supportedRules = [...dockerRules, ...gitRules, ...goRules, ...httpRules];
 export const supportedRulesRegex = regEx(`^${supportedRules.join('|')}$`);
 
-function isTarget(x: Record<string, TargetAttribute>): x is Target {
-  return is.string(x.name) && is.string(x.rule);
-}
-
-export function coerceFragmentToTarget(fragment: Fragment): Target | null {
-  if (fragment.type === 'record') {
-    const { children } = fragment;
-    const target: Record<string, TargetAttribute> = {};
-    for (const [key, value] of Object.entries(children)) {
-      if (value.type === 'array') {
-        const values = value.children
-          .filter((x): x is StringFragment => x.type === 'string')
-          .map((x) => x.value);
-        target[key] = values;
-      } else if (value.type === 'string') {
-        target[key] = value.value;
-      }
-    }
-
-    if (isTarget(target)) {
-      return target;
-    }
+export function extractDepFromFragmentData(
+  fragmentData: FragmentData
+): PackageDependency | null {
+  const res = Target.safeParse(fragmentData);
+  if (!res.success) {
+    return null;
   }
-
-  return null;
+  return res.data;
 }
 
 export function extractDepFromFragment(
   fragment: Fragment
 ): PackageDependency | null {
-  const target = coerceFragmentToTarget(fragment);
-  if (!target) {
-    return null;
-  }
-
-  const dependencyExtractor = dependencyExtractorRegistry[target.rule];
-  if (!dependencyExtractor) {
-    logger.debug(
-      `Bazel dependency extractor function not found for ${target.rule}`
-    );
-    return null;
-  }
-
-  return dependencyExtractor(target);
+  const fragmentData = extract(fragment);
+  return extractDepFromFragmentData(fragmentData);
 }
diff --git a/lib/modules/manager/bazel/types.ts b/lib/modules/manager/bazel/types.ts
index 10db409ac852c1bbc93751651b4e12b07a62dd57..951abee9c4ba6aa5e430b281619492538ef86ec2 100644
--- a/lib/modules/manager/bazel/types.ts
+++ b/lib/modules/manager/bazel/types.ts
@@ -36,3 +36,8 @@ export interface StringFragment extends FragmentBase {
 
 export type NestedFragment = ArrayFragment | RecordFragment;
 export type Fragment = NestedFragment | StringFragment;
+
+export type FragmentData =
+  | string
+  | FragmentData[]
+  | { [k: string]: FragmentData };